Angular JS prevent $watch listener - angularjs

I've use dropdown list which render some results via $scope.$watch listener callback in Angular JS controller.
When i change dropdown list other value it works fine.
But in some case i need to keep last selected results(does not need rise that event).
I've tried ng-change and ng-click but it works after
$scope.$watch raised.
Any help would be appreciated

There is an overload on $scope.$watch() where you can also capture the previous value of the watched property.
$scope.$watch(propertyExpression, newValue, oldValue)
Is this what you are looking for? It is hard to read your mind without code samples ;)
If you want to keep even older values, you might want to consider keeping some state in your controller.
BTW, it can be interesting to always use this overload and check first whether the object you are watching really changed using (newValue !== oldValue) because (if I recall correctly) $scope.$watch is also triggered when $scope.$apply is called on a higher level node.

See if this works,
var unregisterWatch =
$scope.$watch('param', function(newVal, oldVal) {
if (newVal === oldVal)
return; //
});
call this function when required, it will cancel Angular's watch on the variable.

Related

angular watch not fired

There is 1 angular app, with 1 parent controller, and a child controller.
In the child, there is 1 $watch WATCH-CHILD for OBJ-CHILD, which triggers an $emit.
In the parent, there is a listener for the $emit, we'll call it ON-LISTENER, and a $watch WATCH-PARENT for OBJ-PARENT (which uses true as the 3rd argument).
When the child's OBJ-CHILD is changed, it triggers WATCH-CHILD, which triggers the $emit.
The parent listener ON-LISTENER is fired, and changes OBJ-PARENT. It also sets some $location properties.
The $watch WATCH-PARENT for OBJ-PARENT is never fired (even though the value has changed), as well as the properties set on $location not changed in the browser URL (I know they are indeed changed inside the JavaScript, cause I print them).
In order to make sure that ON-LISTENER is called within a $digest, I tried to call $digest at the end of ON-LISTENER, and got the expected exception.
Any idea if I'm doing something wrong? I expect the changes that occur in ON-LISTENER to trigger WATCH-PARENT and browser URL change.
I will try to reproduce on jsfiddle and edit this post if successful.
The code looks like:
CHILD:
$scope.$watch('vars.model', function(newValue, oldValue) {
console.log('model changed');
$scope.$emit('highlightChange', newValue);
}, true);
PARENT:
$scope.$watch('vars.model.highlight', function(newValue, oldValue) {
console.log('highlight changed');
}, true);
$scope.$on('highlightChange', function(event, value) {
console.log('listener', $scope.vars.model.highlight.categoryId, value.categoryId);
$location.search('category-id', value.categoryId);
$scope.vars.model.highlight.categoryId = value.categoryId;
}
Next time please provide more code which works, that way you can get better answers.
Here is a Demo plunker which I created to test the code which you provided. It works just fine. If you could provide more code then we could find the real reason why it did not work.
I created two controllers parentCtrl and childCtrl which uses your code and object of provided structure.
$scope.vars = {
model:{
highlight:{
categoryId : 5 //This value is set for testing purposes
}
}
};
Also, I changed watch target (vars.model -> vars.model.highlight) to be the same as in parent controller
$scope.$watch('vars.model.highlight', function(newValue, oldValue) {
console.log('child model changed (new/old)', newValue, oldValue);
$scope.$emit('highlightChange', newValue);
console.log('Emited change event');
}, true);
Thanks for the help. I found out that my event originated from a manual call to $scope.$digest() due to the originating event being triggered from a daterangepicker 'apply.daterangepicker' event.
When I changed that to $scope.$apply, the problem seemed to go away.
I can assume from that, that $apply() is the one in charge of keep calling $watch as long as there are changes, and that $digest() doesn't do so.
For future reference, I placed the problem here:
https://plnkr.co/edit/ItkALhw16Aqukk3EFrRz?p=preview
$scope.digest();
should become
$scope.apply();

$scope.$watch with non existing variable as value

I've been trying to understand $watch function on $scope object. This looks pretty straight forward but the thing that I don't understand is why (on page load) listener function is being executed when I pass a non existing $scope object variable in value function.
$scope.$watch ('nonExistingVariableIdentifier', function () { console.log('Variable value changed'); });
Am I missing something?
The watch runs when it is created.
The full use of a $watch is:
$scope.$watch("nonExistantVariable", function(newValue, oldValue) {
if (newValue == oldValue) {
// First run
}
else {
// After First run
}
})
This is the correct way to differentiate between the initialization and an actual change.
After a watcher is registered with the scope, the listener fn is called asynchronously (via $evalAsync) to initialize the watcher. In rare cases, this is undesirable because the listener is called when the result of watchExpression didn't change. To detect this scenario within the listener fn, you can compare the newVal and oldVal. If these two values are identical (===) then the listener was called due to initialization.
From the Angular JS Docs for $watch - Docs

How can I get notifications of rootScope changes?

I've got a global variable in my rootScope for AngularJS which has properties updated in other various places (outside of angular). For example, lets say the property 'name' is updated on it. It seems like it updates on the root scope fine and after doing an apply or firing a controller function on any child function the view eventually updates, but this is a problem.
How can I get the controllers to update the templates to reflect the rootScope changes immediately?
How can I observe any changes whatsoever on this object, and invoke apply?
You can create a $watch as so:
// Assuming that $rootScope.name exists
$rootScope.$watch('name', function(newValue, oldValue)
{
// You have access to both the newValue
// and the oldValue
});
To $watch for objects, taking this from Angular's Site, a third boolean character is needed
objectEquality (optional) boolean: Compare object for equality rather
than for reference.
// Assuming that $rootScope.obj exists
$rootScope.$watch('obj', function(newValue, oldValue)
{
// You have access to both the newValue
// and the oldValue
}, true);
But know that it is not a good idea to use the $rootScope to store content. You should consider using a service or a factory for that purpose. Read more about this here.
Create a watch for it on rootscope ($rootScope.$watch('varName', function (newValue) {}[,true])).
The varName parameter accepts expressions with '.' for sub-objects and '[]' for array indexes. The third parameter indicates listening fire changes "inside" the object.

Changed value on AngularJS Service not triggering watch within directive

I'm having an issue getting a watch to work within a directive. I've put together a simple example here. http://plnkr.co/edit/A7zbrsh8gJhdpM30ZH2P
I have a service and two directives. One directive changes a property in the service, and another directive has a watch on that property. I expected the watch to fire when the property is changed but it doesn't.
I've seen a few other questions like this on the site, but the accepted solutions on them have not worked here. I've considered using $broadcast or trying to implement an observer, but it seems like this should work and I don't want to over complicate things if possible.
Mark Rajcok' answer is incomplete. Even with angular.copy(), $watch listener will be called once and never again.
You need to $watch a function:
$scope.$watch(
// This is the important part
function() {
return demoService.currentObject;
},
function(newValue, oldValue) {
console.log('demoService.currentObject has been changed');
// Do whatever you want with demoService.currenctObject
},
true
);
Here the plunker that works: http://plnkr.co/edit/0mav32?p=preview
Open your browser console to see that both the directive and the demoService2 are notified about demoService.currentObject changes.
And btw angular.copy() is not even needed in this example.
Instead of
this.currentObject = newObject;
use
angular.copy(newObject, this.currentObject);
With the original code, the viewer directive is watching the original object, {}. When currentObject is set to newObject, the $watch is still looking for a change to the original object, not newObject.
angular.copy() modifies the original object, so the $watch sees that change.

Model update detection

I try to implement an autosaving feature for my forms basing on the $watch function of AngularJS. I initialized watching as described below:
var unbindWatcher = $scope.$watch('selectedElement', function(newValue, oldValue) {
(...)
});
The selected element is loaded using the $resource object and its query method. The selectedElement is set then using one element of the list. Fields of the selectedElement are linked with form elements using ng-model attribute.
The problem is that the callback specified in the $watch method is called once before any update is done.
Is it a normal behavior of the $watch method? How to catch when the selectedElement is actually updated?
Thanks for your help.
Thierry
Yes it is expected behavior After a watcher is registered with the scope, the listener fn is called asynchronously (via $evalAsync) to initialize the watcher. You can add below line to function
var unbindWatcher = $scope.$watch('selectedElement', function(newValue, oldValue) {
if(newvalue===oldvalue) return;
});

Resources