Nitya
Nitya

Reputation: 163

$watch is firing only once inside directive

I am new to angular js, so Please some one help me out.I have my template here:

<form ng-model="signup" form-valid>
    <input type="text" name="username" ng-model="signup.username">{{signup}}
</form>

And my directive is like this:

app.directive("formValid",function(){

 return {
    restrict:"A",
    require:"ngModel",
    link:function(scope,element,attrs){
          scope.$watch(attrs.ngModel,function(newValue){
              if(newValue){
                 console.log(newValue);
              }
          });
       }
   }});

When ever I am entering some value into text box the model is changing and accordingly "$watch" should fired.But in here "$watch" is fired only once when for the first time I enter any value in to text box.Thanks in advance.

Upvotes: 7

Views: 4505

Answers (2)

Michael Benford
Michael Benford

Reputation: 14104

When you use ngModelController, the standard way for watching changes on the model is by creating a formatter:

link: function(scope, element, attrs, ngModelCtrl) {
    ngModelCtrl.$formatters.push(function(value) {
        // Do something with value
        return value;
    });
}

Keep in mind that formatters are only triggered when the model is changed directly. If the change comes from the UI (i.e. the user changes something), then parsers are triggered instead. So you may need to do this as well:

ngModelCtrl.$parsers.push(function(value) {
    // Do something with value
    return value;
});

Working Plunker

I suggest that you read ngModelController documentation so you understand exactly how those pipelines work.

But if all you want to do is get notified when the model changes (you don't want or need to neither format nor parse anything), then you can do something simpler:

scope: { model: '=ngModel' },
link: function(scope) {
    scope.$watch('model', function(value) {
        // Do something with value
    });
}

Working Plunker

But given your directive's name, formValid, I think that using ngModelController is the correct approach.

UPDATE

It's worth mentioning that ngModelController has arguably one shortcoming: it doesn't work very well with "reference" types (e.g. arrays, objects). More details on that can be found here.

Upvotes: 3

Asta
Asta

Reputation: 1579

One solution would be to use ngModel like in the example below.

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

Edit: The quickest solution would be to follow the comment by Blackhole and update your watch to a deep watch by adding true. This is because you are watching the signup attribute but the model value username is a property of signup. The deep watch will serve to watch the properties.

Using ngModel directly rather than via the attrs, or using formatters or parsers (as detailed in Michaels answer is a better solution in my view).

Upvotes: -1

Related Questions