$watch ngModel from inside directive using isolate scope

84,285

Solution 1

The problem is that you $watching attrs.ngModel which is equal to "myModel". You do not have "myModel" bound in your scope. You want to $watch "model". That is what is bound in the scope of your directive. See http://jsfiddle.net/BtrZH/5/

Solution 2

You'll need to watch a function that returns the $modelValue you're watching.

The following code shows a basic example:

app.directive('myDirective', function (){
    return {
        require: 'ngModel',
        link: function(scope, element, attrs, ngModel) {
           scope.$watch(function () {
              return ngModel.$modelValue;
           }, function(newValue) {
               console.log(newValue);
           });
        }
     };
});

Here's a plunker of the same idea in action.

Solution 3

The proper way to do this is:

app.directive('myDirective', function () {
  return {
    require: 'ngModel',
    link: function (scope, element, attrs, ngModel) {

        ngModel.$render = function () {
            var newValue = ngModel.$viewValue;
            console.log(newValue)
        };

    }
  };
});

Solution 4

Here is another way to do this:

app.directive('myDirective', function (){
    return {
        require: 'ngModel',
        link: function(scope, element, attrs, ngModel) {
           attrs.$observe('ngModel', function(value){ // Got ng-model bind path here
              scope.$watch(value,function(newValue){ // Watch given path for changes
                  console.log(newValue);  
              });
           });
        }
    };
});

Doing it that way you will be able to listen value changes with binds like that

Solution 5

This is an extension of @ Emmanuel's answer above to answer @Martin Velez, although I know it's pretty late! (Also I can't make comments yet, so if this isn't the right place for this, sorry!)

I'm not sure which version of Angular OP was using, but in Angular#1.2+ at least on the official docs https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#$render, $render is listed like this:

Called when the view needs to be updated. It is expected that the user of the ng-model directive will implement this method.

The $render() method is invoked in the following situations:

$rollbackViewValue() is called. If we are rolling back the view value to the last committed value then $render() is called to update the input control. The value referenced by ng-model is changed programmatically and both the $modelValue and the $viewValue are different from last time. 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.

I interpret this to mean that the correct way to $watch an ngModel from a directive is to require ngModel and implement a link function that injects ngModelController. Then use the ngModel API that's built in to $render-on-change ($watch), or whatever else.

Share:
84,285
Dustin
Author by

Dustin

Updated on December 21, 2020

Comments

  • Dustin
    Dustin over 3 years

    I am trying to watch my model value from inside my linking function.

    scope.$watch(attrs.ngModel, function() {
           console.log("Changed"); 
        });
    

    When I change the model value inside my controller, the $watch function is not triggered.

    $scope.myModel = "ACT";
    
    $timeout(function() {
       $scope.myModel = "TOTALS"; 
    }, 2000);
    

    Fiddle: http://jsfiddle.net/dkrotts/BtrZH/4/

    What am I missing here?