Steve Scerri February 2016

Angular Directive inside another Directive stops updating when inner directive is edited

So I have a directive inside another directive. Both directives use the same property which is shared using a service and they both have an input to edit this property. The outer directive uses "transclude" to display the inner directive. When editing the outer directive, the inner directive updates correctly. But, when updating the inner directive, the connection seems to get lost.

Here is the code:

var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope, dataService) {
});

myApp.directive('dir1', function(dataService){
  return {
    restrict: 'E',
    transclude:true,
    template: '<h3>Directive 1</h3><input type="text" ng-model="item"/><div ng-transclude></div>',
    link: function(scope, elem, attr) {
      scope.data = dataService;
    }
  };
});

myApp.directive('dir2', function(dataService){
  return {
    restrict: 'E',
    template: '<h3>Directive 2</h3><input type="text" ng-model="item"/>',
    link: function(scope, elem, attr) {
      scope.data = dataService;
    }
  };
});

myApp.factory('dataService', [function(){
  return { item: "" };
}]);

And this is it's view:

<div ng-controller="MyCtrl">
  <dir1>
    <dir2>

    </dir2>
  </dir1>
</div>

I made a JSFiddle to show my problem http://jsfiddle.net/stevescerri/19L24uL6/2/

Any help would be appreciated as I cannot find a work around yet, thanks! :)

Answers


Nikos Paraskevopoulos February 2016

By default directives create new, prototypically inherited scopes. Prototypical inheritance means (in short) that when reading a property that does not exist in this scope, Angular tries to read it from the parent. When writing a property it is always written in this scope. So dir2 creates a scope within the scope of dir1. When dir2 just reads the ng-model property, it reads it from the scope of dir1. When it writes it, it is written to its own scope. dir1 knows nothing about the inner scope, so the directives display a different value as soon as you type something in the "Directive 2" textbox.

Moreover using ng-model="item" does NOT reference the value of the dataService, as it is clearly intended. It merely references an item property of the current scope. Using `ng-model="data.item" will solve your current problem.


Having said the above, it would be a much better idea (IMO) to use Angular's support for custom input controlls - defining your own "extension" of the ng-model as described here under "Custom Control Example". Your code will become something like:

<dir1 ng-model="data.item" />
<dir2 ng-model="data.item" />

This makes your directives less coupled to the actual expression they are editing, at the expense of some extra work. While you're at it, go for isolated scopes too, then your directives become components, truly independent of their context. It the data.item is NOT editable in the directive, just use the isolated scope's = binding.

Post Status

Asked in February 2016
Viewed 3,677 times
Voted 14
Answered 1 times

Search




Leave an answer