Reputation: 4290
I created a simple directive:
var app = angular.module('plunker', []);
function Data(n) {
this.n = n;
}
app.controller('MainCtrl', function($scope) {
$scope.data = [
new Data(1),
new Data(2),
new Data(3)
];
$scope.addNew = function() {
for (var i = 0; i < $scope.data.length; i += 1) {
$scope.data[i].n *= 10;
}
$scope.data.push(new Data(1));
}
});
app.directive('data', function() {
return {
require: 'ngModel',
template: '<div><span>n using scope: {{n}}</span><br><span>n using model: {{model.n}}</span></div>',
restrict: 'E',
link: postLink,
scope: {},
};
function postLink(scope, element, attrs, ngModelController) {
ngModelController.$render = function() {
var $viewValue = ngModelController.$viewValue;
scope.n = $viewValue.n;
scope.model = $viewValue;
};
}
});
Plunker link here.
After calling addNew()
few times I get output:
n using scope: 1
n using model: 1000
n using scope: 2
n using model: 2000
n using scope: 3
n using model: 3000
n using scope: 1
n using model: 100
n using scope: 1
n using model: 10
n using scope: 1
n using model: 1
I understand why scope.n
value is not updated (as it is clearly stated in documentation of ngModelController, but I wonder why scope.model
value is updated?
Upvotes: 2
Views: 140
Reputation: 6221
If you console.log scope.model
and scope.n
, you get the following:
scope.model: Data {n: 1, $$hashKey: "object:21"}
scope.n: 1
I believe the behavior you are seeing is arising because of ng-repeat: angular generates and attaches a $$hashkey to the object, which angular uses to track updates.
The docs say this about $ngModelController's $render method:
Since ng-model does not do a deep watch, $render() is only invoked if the values of $modelValue and $viewValue are actually different from their previous value. If $modelValue or $viewValue are objects (rather than a string or number) then $render() will not be invoked if you only change a property on the objects.
So it seems that $render is getting invoked for scope.model, but not scope.n, but I don't believe it actually is. I think it's updating because of the $$hashkey that is getting attached with the object to scope.model. Because with scope.n you aren't getting the hashkey, it doesn't update. When angular does a $digest it's going to see that $$hashkey and try to update that binding.
If you take out the $$hashkeys it no longer updates. We can strip the $$hashkey by transforming the $viewValue before attaching it to scope.model:
scope.model = angular.fromJson(angular.toJson($viewValue));
and now if you console.log(scope.model) you get:
Object {n: 3}
No more $$hashkey, and the view acts the way you think it would - where $render is not getting invoked.
Upvotes: 1