7

I'm trying to figure out why I'm having trouble overwriting a value passed to an angularJS directive via an isolate scope (@). I try to overwrite the value of vm.index with the following:

vm.index = parseInt(vm.index, 10)

However, it doesn't work for some reason.

If I change it to:

vm.newIndex = parseInt(vm.index, 10)

It works. Also, assigning the value on the $scope works.

Why doesn't the first method work?

I've created this example plunker for reference.

Raphael Rafatpanah
  • 19,082
  • 25
  • 92
  • 158

2 Answers2

7

As you used @ here which need value from an attribute with {{}} interpolation directive. And seems like directive is getting loaded first & then the vm.index value is getting evaluated. So the changes are not occurring in current digest cycle. If you want those to be reflected you need to run digest cycle in safer way using $timeout.

$timeout(function(){
  vm.index = parseInt(vm.index, 10)
})

Above thing is ensuring that value is converted to decimal value. The addition will occur on the on the directive html <h2>Item {{ vm.index + 1 }}</h2>

Working Demo

The possible reason behind this

As per @dsfq & my discussion we went through the angular $compile API, & found that their is one method call initializeDirectiveBindings which gets call only when we use controllerAs in directive with an isolated scope. In this function there are switch cases for the various binding @,= and & , so as you are using @ which means one way binding following switch case code gets called.

Code

case '@':
    if (!optional && !hasOwnProperty.call(attrs, attrName)) {
        destination[scopeName] = attrs[attrName] = void 0;
    }
    attrs.$observe(attrName, function(value) {
        if (isString(value)) {
            destination[scopeName] = value;
        }
    });
    attrs.$$observers[attrName].$$scope = scope;
    if (isString(attrs[attrName])) {
        // If the attribute has been provided then we trigger an interpolation to ensure
        // the value is there for use in the link fn
        destination[scopeName] = $interpolate(attrs[attrName])(scope);
    }
    break;

In above code you can clear see that there they placed attrs.$observe which is one sort of watcher which is generally used when value is with interpolation like in our case it is the same {{index}}, this means that this $observe gets evaluated when digest cycle run, That's why you need to put $timeout while making index value as decimal.

The reason @dsfq answer works because he use = provides two way binding which code is not putting watcher directly fetching value from the isolated scope, here is the code. So without digest cycle that value is getting updated.

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • 1
    This is proper approach for the problem. What happens is that controller gets initialized *before* directive scope configuration is expanded. So controller runs first, then scope => index creates controller's this.index from attribute value which is a string. It overwrites anything what you set in controller. – dfsq Jun 23 '15 at 21:33
6

Apparently it has something to do with one-way binding of the scope index value. So Angular won't update scope.index (or this.index in case of bindToController: true) because scope is configured as

scope: {
    index: '@'
},

If you change it to two-way binding like:

scope: {
    index: '='
},

It will work:

<some-directive index="$index"></some-directive>

Demo: http://plnkr.co/edit/kq16cpk7gyw8IE7HiaQL?p=preview

UPD. @pankajparkar made a good point that updating value in the next digest fixed the issue. This approach for the problem then is closer then what I did in this answer.

dfsq
  • 191,768
  • 25
  • 236
  • 258
  • Sorry.but I think you mis-read the question..why the scope thing work and `vm.index` thing is not working,,I mean to say addition on directive html.. – Pankaj Parkar Jun 23 '15 at 20:53
  • Yes, looks like your are right, however it's not clear why next digest is needed in this case. – dfsq Jun 23 '15 at 21:05
  • that is probably because of `@` or rather i can say interpolation directive..directive is getting initalized first & value is populated after evaluation of parser – Pankaj Parkar Jun 23 '15 at 21:07
  • Yes, it definitely related to `@` but I'm not sure about entire machinery in this case. But you made a good point with timeout. – dfsq Jun 23 '15 at 21:08
  • Thanks for you appretiation ..your solution is also cool..but didn''t explained OP's question's answer.. – Pankaj Parkar Jun 23 '15 at 21:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/81334/discussion-between-pankajparkar-and-dfsq). – Pankaj Parkar Jun 23 '15 at 21:12
  • as per our discussion I updated answer..would you mind to look at it..& feel free to edit it..Thanks :) – Pankaj Parkar Jun 23 '15 at 22:34