9

I have a strange behavior in my app using AngularJS 1.5.8:

plunker (https://plnkr.co/edit/zaHVJeK8hdxk2gaOL9Pf?p=preview) and video(http://recordit.co/eszvfdfC9S)

  • step 1. At the beginning changing ng-required doesn't call ng-change function
  • step 2. After making changes in input AND removing them (input is empty) ng-required DOES call ng-change function

expected behavior?

  • step 2. After making changes in input AND removing them (input is empty) ng-required SHOULD NOT call ng-change function. As it was at the beginning, and as it is when input has some value

Please let me know if it's a bug or not. If not then why changing ng-required calls ng-change NOT always or even at all?

ANSWER IS FOUND-------------------------------------------------------------------------------------------------------

NgModelController has two properties: $viewValue (value entered by user) and $modelValue (value bound to your model). When the value entered by the user fails validation, NgModelController sets the model to undefined.

In AngularJS 1.3, they added the ng-model-options directive. It lets you configure how/when the model gets updated. You can use the allowInvalid option to prevent your model from being set to undefined:

ng-model-options="{allowInvalid: true}"
Ivan
  • 852
  • 1
  • 12
  • 29

4 Answers4

7

You should add

        ng-model-options="{allowInvalid: true}"

So the final result will be

<input type="text" 
    ng-change="$ctrl.onChange()" 
    ng-required="$ctrl.isRequired"
    ng-model-options="{allowInvalid: true}"
    ng-model="$ctrl.val"
    />
user2414751
  • 176
  • 1
  • 6
2

This is happening because the ng-required is changing the attached modal value to undefined from blank when the required is set to false, due to this ng-change is fired since the modal changes.

Check in the plunker i have console.log the value of input and you can see the change of modal clearly.

angular.
  module('myApp', []).
  component('greetUser', {
    templateUrl: 'tmpl.html',
    controller: function GreetUserController() {
      this.output='';
      this.isRequired = false; 

       console.log(this.val);

      this.onChange = function() {

        console.log(this.val);
        this.output+='Changed\n';
      }
    }
  });

plunker : https://plnkr.co/edit/6IeIjIDahcmBIU4KSASJ?p=preview

Now the question arises that why not the on load/ first time the change event is not firing up that is because we are using this object rather then $scope.

Here 'this' vs $scope in AngularJS controllers

is a very good example which explains why until we manually enter the value in the input at least once the change event is not firing up.

in short this is happening because ng-change/ng-model works with scope variables. Once you manually enter value in the input element, the model binding happens with the scope, and the ng-change event start firing up.

Community
  • 1
  • 1
Deep
  • 9,594
  • 2
  • 21
  • 33
  • thanks for your answer but that's not correct. Please check the correct answer – Ivan Nov 24 '16 at 09:48
  • Thanks :) my answer was more emphasized why is this happening. Unfortunately this is not the answer your are looking for – Deep Nov 24 '16 at 09:53
  • 1
    I see what you meant, and it's correct, I've checked again. Thanks for help. But still I think this is incorrect behavior in Angular – Ivan Nov 24 '16 at 10:12
  • 1
    here's what I found: `NgModelController has two properties: $viewValue (value entered by user) and $modelValue (value bound to your model). When the value entered by the user fails validation, NgModelController sets the model to undefined.`, That's exactly what you explained, thnks – Ivan Nov 24 '16 at 10:14
0

I think you misunderstanding.

ng-required and ng-change is different things. doing different purpose, and not calling each other.

ng-change is calling your function no matter what it's empty or not. It's call your method by it self, regarding to changes happening in the input.

ng-required is just checking value if it's empty or not, If it is empty, mark it as invalid.

In order to get what you want, you have to check the validity inside the onChange function.

this.onChange = function() {

    if( !$scope.$ctrl.form.$valid ) return;

    this.output+='Changed\n';
}
Hereblur
  • 2,084
  • 1
  • 19
  • 22
  • you wrote `and not calling each other`, but that's the problem: `ng-required` calls `ng-change`. – Ivan Nov 24 '16 at 09:40
  • I cannot check if form is valid because it's dynamic and a lot of other valid/invalid fields – Ivan Nov 24 '16 at 09:41
0

I think this is the reason. put an alert inside ng-change like this.

this.onChange = function() {
        alert(this.val);
        this.output+='Changed\n';
      }

When you empty the box after completing it the value change between two values:

undefined  when is required 
''         when is not required 

So By changing the radio box you call the ng-change, At the beginning you have

However when you have not started to type in the text box , radio box does not change the input value , because ng-change is for input. In the beginning we have undefined --> undefined so nothing changed. then when you empty the input you have '' ---> undefined.

Actually if you put this in your controller you get call ng-change at the beginning too.

this.val ='';

So if you replace your controller with this , you see ng-change is called even at the beginning.

angular.
  module('myApp', []).
  component('greetUser', {
    templateUrl: 'tmpl.html',
    controller: function GreetUserController() {
      this.output='';
      this.isRequired = false; 
      this.val ='';
      this.onChange = function() {
        this.output+='Changed\n';
      }
    }
  }); 
Farzad Salimi Jazi
  • 760
  • 10
  • 25