In my custom directive, I'm adding elements to the DOM based on the number of objects in my datasource array. I need to watch a specific property in each object. As I add these elements to the DOM, I want to set up a $watch on the checked
property of each object in the toppings
array, but it's not working, and I don't know why. I set up a breakpoint inside the function that should be invoked when the property changes from true to false or false to true, but that function is never invoked. Is the reason obvious? I'm just learning Angular, so I could easily be making a stupid error.
$scope.bits = 66; (i.e. onions and olives)
$scope.toppings = [
{ topping: 1, bits: 2, name: 'onions' },
{ topping: 2, bits: 4, name: 'mushrooms' },
{ topping: 3, bits: 8, name: 'peppers' },
{ topping: 4, bits: 16, name: 'anchovies' },
{ topping: 5, bits: 32, name: 'artichokes' },
{ topping: 6, bits: 64, name: 'olives' },
{ topping: 7, bits: 128, name: 'sausage' },
{ topping: 8, bits: 256, name: 'pepperoni' }
]
Each object in the model gets a new checked
property which will be true or false.
NOTE: the object array will at most contain a dozen or so items. Performance is not a concern.
link: function link(scope, iElement, iAttrs, controller, transcludeFn) {
<snip>
// At this point scope.model refers to $scope.toppings. Confirmed.
angular.forEach(scope.model, function (value, key) {
// bitwise: set checked to true|false based on scope.bits and topping.bits
scope.model[key].checked = ((value.bits & scope.bits) > 0);
scope.$watch(scope.model[key].checked, function () {
var totlBits = 0;
for (var i = 0; i < scope.model.length; i++) {
if (scope.model[i].checked) totlBits += scope.model[i].bits;
}
scope.bits = totlBits;
});
});
<snip>
Array of Objects:
$scope.toppings = [
{ topping: 1, bits: 2, name: 'onions' },
{ topping: 2, bits: 4, name: 'mushrooms' },
{ topping: 3, bits: 8, name: 'peppers', checked:undefined /*may be*/ }
];
Watch using AngularJs $WatchCollection:
Instead of monitoring objects
array above, that can change for any property in the object, we will create an array of properties of the elements for which we are watching the collection (.checked
).
We filter
the array's elements to only monitor those elements which have .checked
defined and map
that to an array for angular watchCollection
.
When a change fires, I will compare the old and new arrays of (.checked
) to get exact changed element using lodash difference method.
$scope.$watchCollection(
// Watch Function
() => (
$scope
.toppings
.filter(tp => tp.checked !== undefined)
.map(tp => tp.checked)
),
// Listener
(nv, ov) => {
// nothing changed
if(nv == ov || nv == "undefined") return;
// Use lodash library to get the changed obj
let changedTop = _.difference(nv,ov)[0];
// Here you go..
console.log("changed Topping", changedTop);
})
You use MAP to collect all of the property values you need + convert them into a small string representation (in this case 1 and 0) and then join them together into a string that can be observed.
A typescript example:
$scope.$watch(
() => this.someArray.map(x => x.selected ? "1" : "0").join(""),
(newValue, oldValue, scope) => this.onSelectionChanged(this.getSelectedItems()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With