2

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>
4
  • what do you want to achieve ?? Commented Jul 19, 2013 at 10:03
  • I want to know whether model text is empty or not and apply some logic such as adding class to root element of directive etc.. Commented Jul 19, 2013 at 10:15
  • 1
    Best way is to pass the parameter iWantToWatchThisInMyDirective in attribute and watch that attribute but still if you dont want that then you can watch the model in parent controller and broadcast a event in the parent controller and listen to that event in the directive Commented Jul 19, 2013 at 10:29
  • @Ajaybeniwal Nice tip! I also found that angular.element provide scope() method, so scope is accessible that way.. Now just find out content of ngModel / ngBind in element - at best as $attrs in normalized form, so I don't have to try every alternative (data-ng-model, x-ng-model atc..).. Commented Jul 19, 2013 at 11:03

2 Answers 2

0

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();
  });
}
Sign up to request clarification or add additional context in comments.

Comments

0

Here's another way you can do this using $scope.$watch. This code is written without jQuery.

angular.module('sample').directive('sampleDirective', function () {
    'use strict';

    // Returns a JQLite object for the first input element we transcluded, 
    // allowing us to pick information from it
    function findInput(clone) {
        for (var i = 0; i < clone.length; i++) {
            if (clone[i].nodeName.toLowerCase() == 'input') {
                return angular.element(clone[i]);
            }
        }
    }

    return {
        transclude: true,
        link: function (scope, element, attrs, ctrl, transclude) {

            transclude(function (clone) {
                var input = findInput(clone);
                if (input) {

                    // Watch for changes on our select element's model
                    scope.$watch(input.attr('ng-model'), function (val) {

                        // Do things
                        console.log('My updated model:', val);
                    });
                }
            });
        }
    };
});

This assumes your model is available on the same scope the directive is on. If your directive has an isolate scope, you could alter the watch statement to watch for the ng-model string on the $parent scope:

scope.$watch('$parent.' + input.attr('ng-model')

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.