Bill
Bill

Reputation: 25565

Implementing a change handler for Angularjs directive

I'm trying to create a custom directive that provides similar change notification as the input control. With input controls, you can do the following:

<input type="text" ng-model="foo" ng-change="bar(a,b)">

In this case, the bar function specified with ng-change will only be called once every time the value of foo is changed. I created a custom directive and I want to add the same type of change notification.

<div my-directive ng-model="foo" ng-change="bar(a,b)"></div>

I first tried to include the change handler in the scope and then execute it, but including it in the scope causes it to be called 12 times when it is instantiated and 12 times when it changes because I believe that is how often the attribute is being evaluated.

return {
  templateUrl: '...',
  restrict: 'A',
  require: '^ngModel',
  scope: {
    ngModel: '=',
    ngChange: '='
  },
  link: function postLink(scope, element, attrs) {
    if(angular.isFunction(scope.ngChange) {
      scope.ngChange(); // bar is called 12 times
    }
  }
}

Then I looked at the Angular source code and saw that they eval the value of the attribute for the change handler on input. I tried to do that, but the specified function never seems to get called.

return {
  templateUrl: '...',
  restrict: 'A',
  require: '^ngModel',
  scope: {
    ngModel: '='
  },
  link: function postLink(scope, element, attrs) {
    scope.$eval(attrs.ngChange);  // bar is never called
  }
}

What would be the right way to do this?

Upvotes: 0

Views: 1393

Answers (2)

htellez
htellez

Reputation: 1273

Use the next

return {
    templateUrl: '...',
    restrict: 'A',
    require: '^ngModel',
    scope: {
        ngModel: '=',
        ngChange: '&'
    },
    link: function postLink(scope, element, attrs) 
    {
        scope.changingModel = function(newValue)
        {
             scope.ngModel = newValue;
             scope.ngChange();
        }
    }
}

'&' is used to bind expressions. This will get your ngChange executed.

Using watchers is expensive. A watcher implies that angular will be checking for changes in your variable many, many times. If your variable is an array, it is worse, you must be very careful on the way you update your variable and be sure not to update your variable making changes on it but updating the whole thing at the same time. Something like:

var newValue = getMyNewValue($scope.foo);
$scope.foo = newValue;

The 'link' function is executed before any binding. Be aware of that.

I have been having a slight different problem that I assume you will face and that I hope someone else could solve for both of us.

In my case, I'm ussing something like

<div my-directive ng-model="foo" ng-change="bar(foo)"></div>

The execution of ngChange() will occur in the parent scope, that is in the scope where your bar function is defined. That is the expected behavior. But will be executed as if your model hasn't changed in your parent scope. I mean, bar(foo) will be executed with the old value of foo instead of the newValue.

Upvotes: 0

MRB
MRB

Reputation: 3822

If you want to call ngChange every time that your model has been changed you can watch your model and call ngChange in your watch callback.

Plunk here

Upvotes: 1

Related Questions