Search code examples
angularjsangularjs-directivetransclusion

AngularJS - Watch for model of transcluded element


In AngularJS, I have directive with basic transclusion. I know that usually is transcluded input or textarea when i using it, and if is there, I want to $watch its model for changes. But I don't have access to attribs of transcluded content, only access to attribs of root element which directive is called on. Transcluded scope as well (maybe scope.$$nextSibling can help but something tells me that it's way to hell :) ).

So is there any way to do that without adding another parameter (attribute) to element where is directive called?

directive template

<div ng-transclude>
    <someContent>...</someContent>
    <!-- HERE IS INPUT TRANSCLUDED -->
</div>

directive usage

<div my-directive="somedata">                           //this attribs are accessable
    <input ng-model="iWantToWatchThisInMyDirective" />  //but i want to access this also
</div>

Solution

  • Here is my solution:

    I created second directive: Input (restricted for Element, so every input has one). In Input directive I broadcast every change to element's scope:

    link: function (scope, element: JQuery, attrs: ng.IAttributes) {
      if(typeof attrs.ngModel !== "undefined") {
        scope.$watch(attrs.ngModel, function (newValue, oldValue) {
          scope.$broadcast('valueChanged'+scope.$id, newValue, oldValue);
        });
      }
    }
    

    scope.$id is used just for be sure that event name is unique for every input.

    Now, in any other directive I can listen event of changing any input:

    link: function (scope, element:JQuery, attrs:ng.IAttributes) {
      //My input is last child everytime..
      var children = element.find("input");
      var lastInput = angular.element(children[children.length - 1]); 
      var lastInputScope = lastInput.scope();
    
      var unregister = lastInputScope.$on('valueChanged' + lastInputScope.$id, 
                                          function (event, newValue, oldValue) {
        //do whatever you want...
      });
    
      scope.$on('$destroy', function () {
        unregister();
      });
    }