AngularJS $watch iterator - angularjs

Is there a way to access the angular $watch iterator?
I want to capture the first iteration through a $watch like this:
scope.$watch("var", myFunc);
myFunc() {
if (iterator === 1) ...
}

Your watch function will get two arguments, the current value of "var" and the previous value. When your watch fires the first, both of these will be the same, so you can check for that to know if it's the first time it's called. Like this:
var myFunc = function(value, oldValue) {
if (value === oldValue) {
// First run
}
}

Related

Watching service object for changes is not working when using the controller as syntax

I tried to follow along with this example but my code never enters the callback with the newValue when the input changes for some reason. The only difference in my example and the example bin below is that I'm using a service to hold the value instead of a controller variable.
I've been trying to make it work but I can't wrap my head around it. What am I missing?
http://jsbin.com/yinadoce/1/edit?html,js,output
Note that I've excluded the input and the steps prior to the value being updated in the service as this works perfect. The issue is only that my watch doesn't understand when the value has changed in the service.
EDIT: Forgot to inject $scope in the controller when I pasted over the code, now it's complete.
Controller:
coForms.controller('CoFormsCtrl', ['$scope', 'coFormsInfo', function($scope, coFormsInfo) {
$scope.$watch(angular.bind(coFormsInfo.getInfo(), function() {
return coFormsInfo.getInfo();
}), function(newVal) {
console.log(newVal);
});
}]);
Service:
coForms.service('coFormsInfo', [function() {
var info = {
filteredList: []
}
this.setFilteredList = function(list) {
info.filteredList = list;
};
this.getInfo = function() {
return info;
};
}]);
The watcher is there to detect any changes in the variable you're watching. How can he watch something that is not... Strictly present like a return value?
I'm not sure about what I'm saying because I'm new to angular, but the logic seems false there. You need to watch something declared to detect some changes.
You should call your service to get your infos when you need them and watch for an info variable.
EDIT
My bad there is something like that but you should declare it in a function maybe like the example on the documentation
var food;
scope.foodCounter = 0;
expect(scope.foodCounter).toEqual(0);
scope.$watch(
// This function returns the value being watched. It is called for each turn of the $digest loop
function() { return food; },
// This is the change listener, called when the value returned from the above function changes
function(newValue, oldValue) {
if ( newValue !== oldValue ) {
// Only increment the counter if the value changed
scope.foodCounter = scope.foodCounter + 1;
}
}
);

$watch callback execution order

Here's what I have:
$scope.$watch('someVar', function (val) {
$scope.someList.push(val);
execLongFunction.then(function (data) {
val.computedValue = data;
})
})
What I want: to add a value to the array in my scope, then perform some calculations and then (maybe) update the scope. The main point is that I want val to be pushed to scope array before execLongFunction is done.
The problem is: all $watch callback code is executed, kind of, immediately: even if I add some console.log() I see all of them at the same time.
What am I doing wrong?
$scope.$watch('someVar', function (newVal,oldVal) {
if(newVal != oldVal){
$scope.someList.push(newVal);
execLongFunction.then(function (data) {
val.computedValue = data;
})
}
});
$watch returns both oldValue and newValue of the $scope variable you are watching. Try to use those and execute your functions accordingly. This should work if the value of 'someVar' doesn't change every time when updated from the directive.

What does it mean watch expression missing

Newbie quesiton, I read the document of rootscope.$watch. the syntax of $watchis
$watch(watchExpression, [listener], [objectEquality], [deregisterNotifier]);
But I see the examples use it like below.
var food;
scope.foodCounter = 0;
expect(scope.foodCounter).toEqual(0);
scope.$watch(
// This is the listener function
function() { return food; },
// This is the change handler
function(newValue, oldValue) {
if ( newValue !== oldValue ) {
// Only increment the counter if the value changed
scope.foodCounter = scope.foodCounter + 1;
}
}
);
What does it mean missing WatchExpression? thanks.
watchExpression can be both string or function - refer back to angularjs documentation as posted in the question. Given example as posted in the question will never trigger change handler because food is always undefined.
If you are using function as watchExpression, the function must return different value in order to trigger change handler. As documented in angularjs official document
Expression that is evaluated on each $digest cycle. A change in the
return value triggers a call to the listener.
Code Snippet
function Ctrl($scope) {
$scope.food = "Laksa";
$scope.foodCounter = 0;
$scope.$watch(
//this is watch expression using function
function (scope) {
return scope.food;
},
//this is listener - change handler
function (newValue, oldValue) {
console.log(newValue, oldValue);
if (newValue !== oldValue) {
// Only increment the counter if the value changed
$scope.foodCounter = $scope.foodCounter + 1;
}
});
}
example - http://jsfiddle.net/4atA2/2/
this listener function is equivalent to $scope.$watch('food',... because it don't have additional logic. You have flexibility to add additional logic checking to decide whether want to trigger change handler or not. For example, watch on two scope variable.
example - http://jsfiddle.net/5NLNa/5/
function (scope) {
if(scope.drink == "Milo") {
return scope.food;
}
},

Angular service watch creates $digest loop

I am trying to get a scope to update when the return value of a service function changes. From reading, it sounds like I'm supposed to use $watch, but following the angular docs creates an infinite $digest loop in my application, here the controller:
.controller('navCtrl', function(CookieHandler, $scope){
// this works, but doesn't update when CookieHandler.get() changes
// $scope.user = CookieHandler.get();
$scope.$watch(
function() {return CookieHandler.get()},
function(newValue, oldValue) {
if( newValue !== oldValue) {
$scope.user = newValue
}
}
)
//etc
From the error message there's something about using $watch on functions that return an array, but CookieHandler.get() returns an object?
I looked around some more and read that I should use $apply instead of $watch here because it's easier to test, so I tried this:
.controller('navCtrl', function(CookieHandler, $scope){
// this works, but doesn't update when CookieHandler.get() changes
// $scope.user = CookieHandler.get();
$scope.$apply(function(scope){
scope.user = CookieHandler.get();
})
})
But this throws an error about the digest cycle already running. Is there a way to fix my $watch to stop the infinite loop? CookieHandler.get just checks the value with $cookieStore
$watch performs an equal comparison between the current value and the value from the previous digest cycle. If they are different then the function is executed.
If the object returned by CookieHandler.get() is always the same reference then the watch function will never get called. Even if you update properties of that object.
Arrays are a bad idea because they are often always a new object and JavaScript will see them as never equal.
You could serialize the object/array to JSON and use that value, but this won't work if there are changes in key/value orders.
It looks like you are trying to monitor when the current user is changed. Assuming you have a unique identifier for a user, then you might want to try something like this.
$scope.$watch(
function() {
var user = CookieHandler.get();
return (typeof user === 'undefined') ? 0 : user.id;
},
function(newValue, oldValue) {
if( newValue !== oldValue) {
$scope.user = CookieHandler.get();
}
}
);
Note that I call .get() twice. I don't know how your service works. Maybe that's not possible, but the point is to watch a value that is easily comparable.

Angularjs adding $scope.$watch after $routeProvider's resolve

I'm having an issue where I am trying to watch a certain element from my scope after the resolve is done. For some reason, it gets run when i run the second line from this snippet and I dont seem to be able to add this $watch during the "resolve".
I understand that promises are asynch, but how can I know when my resolve is done to then add the watch?
The variableToWatch can be changed either by code or in the UI (otherwise i would've just used ng-change="doWork()")
$scope.variableToWatch = $route.current.locals.data.initialValue;
$scope.listOfDependantData = $route.current.locals.data.items;
$scope.$watch('variableToWatch', function (newValue) {
myService.getNewDependantData(newValue).$promise.then(
function (items) {
$scope.listOfDependantData = items;
}
);
};
Update:
If you want to run your code only when it changes after the initial setting of value, you can use the second form of the watchFunc passed as the second argument to $scope.$watch:
$scope.$watch('variableToWatch', function (newValue, oldValue) {
if (typeof newValue !== 'undefined' && newValue !== oldValue) {
// Use the new value here ...
}
});
I don't fully follow the question, but I suspect that you want to watch the value $route.current.locals.data.initialValue instead of $scope.varibleToWatch?
In that case, you can use the alternate form of $scope.$watch with a function as the first argument:
$scope.$watch(function () { return $route.current.local.data.intialValue; }, function (newValue) {
// ...
};
Or, if you want to watch the variable on your $scope which is referenced to by the sting contained in $route.current.local.data.initialValue, then you can use this:
$scope.$watch(function () { return $scope[$route.current.local.data.intialValue]; }, function (newValue) {
// ...
};
Does this solve your problem?

Resources