csharpfolk
csharpfolk

Reputation: 4290

AngularJS updating data displayed in directive

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

Answers (1)

tpie
tpie

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.

Plunker

Upvotes: 1

Related Questions