AngularJS : deep watch for map - javascript

How can I deep watch for map?
$scope.map = new Map();
$scope.$watch('map' function() {
// Do Something
});
I would like to fire $watch when elements of the $scope.map has added or deleted or changed.
In case of object, I know I can deep watch by $scope.$watch('obj', function(){}, true). But I don't know how to deep watch for map.
How can I do this?

You can also try to use $watchCollection instead of $watch.
From the Documentation
Shallow watches the properties of an object and fires whenever any of
the properties change (for arrays, this implies watching the array
items; for object maps, this implies watching the properties). If a
change is detected, the listener callback is fired.
The obj collection is observed via standard $watch operation and is
examined on every call to $digest() to see if any items have been
added, removed, or moved.
The listener is called whenever anything
within the obj has changed. Examples include adding, removing, and
moving items belonging to an object or array.

Are you sure the deep watch dont work for js Map ?
from $watch documentation :
When objectEquality == true, inequality of the watchExpression is
determined according to the angular.equals function. To save the value
of the object for later comparison, the angular.copy function is used.
This therefore means that watching complex objects will have adverse
memory and performance implications.
angular.equals work for js maps. So the deep watch should be fine with maps.

Related

Observe change of object proprty in nested array in Ember.js

We have this data structure
[[{foo:"12", bar:"34"}, {foo:"33", bar:"55"}], [{foo:"45", bar:"67"}]]
How is it possible to make a computed property to observe a change on foo or bar?
Normally you use .#each.someProperty in a computed property to watch specific object properties in an array but in the case of nested arrays (or nested objects), you can't do .#each.#each.someProperty because .#each only works one level deep.
You need to use intermediary computed properties to raise the inner objects up one (or more) levels and have another computed property watch these in turn. There may be other ways to wire things up, but this is a pretty straightforward way to do it.
Here's a twiddle that demonstrates it. If you open the console you can see the individual computed properties recording a change occurring.
You can also use an alias to pull the inner arrays / objects up a level or two.

Extjs 4.1 - How to copy Listeners from Object A to Object B

Anyone knows how to copy listeners from Object to another?
I tried
Object a,b (pre-created...)
b.events = a.events
b.hasListeners = a.hasListeners
But it didnt worked as i expanded Object b it didnt stop in the breakpoint i had in the function i had listening for expand, is there any "good way" of doing it?
You shouldn't directly link ExtJS-generated object hierarchy subtrees into other objects; it's bad style and can result in all kind of problems, like memory leaks or premature destruction.
E.g. if one object is destroyed, but the other lives on, what happens to the shared events object?
It may or may not be destroyed by the destructor, and then the other object tries to access it, which results in undefined behaviour.
If you need the same listeners on multiple objects, you should create a prototype that you derive these objects from, or a function that adds exactly these listener functions to whatever object you provide as a parameter.
For your example, because nodes can be auto-created by a reader, I would use the second approach, adding the function on the store that contains the nodes:
addListenersToTreeNode: function(node) {
node.on('collapse', this.onCollapse);
node.on('expand', this.onExpand);
node.on('example', this.onExample);
}
then call the function on both nodes:
nodeStore.addListenersToTreeNode(a);
nodeStore.addListenersToTreeNode(b);
and voilá, the tree nodes now have the same listeners.
define your code in class.
Ext.define('appname.myapp.formname.form',{
xtype:'anyname',
listeners:{
click........
keyup.....
}
});
use your xtype where you

What is "=*" in AngularJS

I've come across this way of isolate binding specification:
scope: {
property: "=*"
}
What does the asterisk mean here? Can someone please provide an example?
The isolate binding with =* is to shallow watch for the change in the collection.
Let me explain a bit:
Normally watchCollection variables we use in the scripts like below:
$scope.arr = ['foo', 'bar', 'lorem', 'ipsum'];
$scope.arrCount = 4;
$scope.$watchCollection('arr', function(new, old) {
$scope.arrCount = new.length;
});
But what if you want to do object binding in html attribute itself?
<my-directive my-attr="arr"><!--$scope.arr-->
And, if you do this:
scope: {
myAttr: "=*"
}
Now, the directive attributes are assigned as it should shallow watch. And the use of watchCollection is good of use this time.
So, the =* is to be used when we need to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use =* or =attr (=? or =*?attr if the property is optional) as described in the docs.
Although, we can use = for watchCollection to deeply watch the object or array we can use =* for the same. But only difference is that using =* method the collection becomes true and when collection becomes true angularjs use the $watchCollection to remove the watcher and it uses the $watch to remove the watcher when collection is false:
if (definition.collection) { //using `=*`
removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
} else { //using `=`
removeWatch = scope.$watch($parse(attrs[attrName],
parentValueWatch), null, parentGet.literal);
}
So, when you want to shallow watch for the collection then you need to use =*.
The asterisk is used to change the default watch behavior.
From the angular docs:
By default, the $watch method is used for tracking changes, and the equality check is based on object identity. However, if an object literal or an array literal is passed as the binding expression, the equality check is done by value (using the angular.equals function). It's also possible to watch the evaluated value shallowly with $watchCollection: use =* or =attr (=? or =*?attr if the attribute is optional).
According to Doc:
= or =attr - sets up a bidirectional binding between a local scope property and an expression passed via the attribute attr. '
By default $watch method is used for tracking changes, and the equality check is based on object identity.
In the case of =* is used to watch evaluated value shallowly using $watchCollection.
There is difference in evaluation using $watch vs $watchCollection.
The following link gives down the difference between them.

AngularJS : $watch property on all objects in array

I am trying to understand how best to achieve the following in angularjs.
1) I have an array of objects:
$scope.myArray = [{special:false, name:'alice'}, {special:false, name:'bob'}];
if myArray[0].special changes to true, using $watch how can I detect that this was because of a change in element 0 of the array. Everything I have read on $watch deals with notifying that the array as a whole as changed but there is no way to retrieve the element/index of the array that has changed. I want to have this cope with varying lengths of arrays, so it would not suffice to simply have:
$scope.watch('myArray[0].special')
2) I have a dictionary:
$scope.myValues = {id1:{special:false, name:'alice'}, id2:{special:false, name:bob'}};
a similar question - if I was to change myValues.id1.special to true, how could I get the corresponding name field (or know it was id1)?
I would really appreciate any help and guidance here on best practice and what is actually possible with angularjs and $watch (or $watchcollection) with respect to arrays and maps. I think this is similar to question AngularJS : Watch array of objects and animate the changed property but I'm also interested to see how to watch across all properties in a dictionary.
Many many thanks in advance.
A watch listener provides you with both the newValue and the oldValue - so it's your job to compare the two to see what has changed. Since you wish to perform value rather than reference comparison, use $watch with the third parameter set to true:
$scope.$watch( 'myObject', function( newVal, oldVal ){
// Compare newVal and oldVal here to see what has changed.
}, true);

angularjs broadcast changes to an object in a service

Suppose I have a service that returns an object with a few values. This is my settings object. I use this service in controller 1 to get the values from the object and assign it to the $scope. In controller2 I use the same service to add a few more values to the object. Now I need controller1 to realize that those changes have been made and update the $scope accordingly.
I tried using $watch in controller1 like so
$scope.$watch(myservice.settings, function(oldvalue,newvalue) {
console.log(newvalue);
});
But it seems that the digest cycle isn't run when the object is updated in controller2. Apart from manually running the $digest cycle, which as I understand it is generally bad parctice, how else can I get the functionaliy I'm looking for?
$watch accepts a function which returns a value or a string to be evaluated as an expression:
It also will not handle internal comparison for objects unless you pass true as a third argument:
$scope.$watch('myservice.settings', function(oldvalue,newvalue) {
console.log(newvalue);
}, true);
This will perform a deep comparison using angular.equals instead of comparing for reference equality, which is the default. If you need to perform only a shallow comparison (ie your object is only one level deep), you can use $watchCollection instead:
$scope.$watchCollection('myservice.settings', function(oldvalue,newvalue) {
console.log(newvalue);
});
The advantage of $watchCollection over $watch(func, func, true) is that it is less expensive.

Categories