Plunkr here: https://plnkr.co/edit/aWBE55?p=preview
Let's say I have a view like this:
<body>
<div ng-app="myApp">
<div ng-controller="myAppController as app" >
<input type="text" ng-model="app.colorName" /><button ng-click="app.addColor()">add color</button>
<div ng-repeat="c in app.colors">{{c.colorName}}</div>
</div>
</div>
</body>
and I have a controller like this:
var Color = function(options) {
var self = this;
self.colorName = options.colorName;
};
(function(angular) {
'use strict';
angular.module('myApp', []).controller('myAppController', function myAppController($filter) {
var self = this;
self.colorName = '';
self.colors = [
new Color({colorName: 'Red'}),
new Color({colorName: 'Blue'}),
new Color({colorName: 'Green'})
];
self.addColor = function() {
console.log("added a new color " + self.colorName);
self.colors.push(new Color({colorName: self.colorName}));
};
setTimeout(function() {
console.log("added new color in another thread");
self.colors.push(new Color({colorName: 'Purple'}));
}, 1000);
});
})(window.angular);
See that little setTimeout guy pushing another color onto the array? It seems that because it's being added from an async call (because of the setTimeout), the view does not refresh immediately and show the Purple color after it's been added.
Interestingly, even clicking into the textbox and then out (which touches that ng-model property) triggers a view update and 'Purple' appears... and, of course, adding a color by pushing the button refreshes the view immediately.
Why does this happen and what should I do about it?
I could do $scope.$apply, but that seems particularly hideous as the real world example (which involves modal dialogs with callbacks) is component-based and trying to get away from doing $scope-y things. This example was worked up for simplicity.
$timeout()
instead ofsetTimeout()
. Angular has no way of knowing when a setTimeout is finished so it cannot update your bindings – rob 23 hours agosetTimeout()
still runs in the same thread as the rest of your app – rob 23 hours ago$scope.$apply()
– rob 23 hours ago$scope.$apply()
is going to be applied is safer to be applied inside a$timeout
in order to avoid a possible $digest error – Asiel Leal Celdeiro 23 hours ago