I'm trying to watch the value of ng-model whilst also using the minlength validation. The problem is the model value remains empty/undefined until the validation criteria is met.
HTML
<input ng-model="xyz" minlength="8" />
JS
$scope.$watch('xyz', function(val) {
// Will either be undefined or a
// string bigger than or equal
// to 8 characters.
console.log(val);
});
I know I could just substring the element's value, but this code is implemented in a directive which uses $compile, so ideally I'd prefer to watch the model value.
Any thoughts on how to resolve this?
https://docs.angularjs.org/api/ng/directive/ngModelOptions
allowInvalid: boolean value which indicates that the model can be set with values that did not validate correctly instead of the default behavior of setting the model to undefined.
Related
Problem:
I am trying to use $scope.$watch method over a ng-model used in a number input with strict attributes like, max, min, maxlength and step.
When the value inserted in the input exceeds any of those attributes, the ng-model retrieves undefined.
So, eventually, what happens is: $scope.$watch is triggered every time we change value in the input. Once the value is undefined, it will only be triggered again when the inserted value is again valid, in other words, following attribute rules (max, min, maxlength and step).
E.g.
user input: -2,4
-> $scope.$watch is triggered, and outputs, newValue as undefined.
user adds a new digit: -2,44
-> $scope.$watch is not triggered anymore, this way.
main.js
$scope.$watch("user.input.base.sphere", function(newValue, oldValue) {
console.log(newValue);
}
**index.html
<input
ng-cloak
type="number"
ng-class="user.settings.input.sphere.class"
autocomplete="off"
required
name="sphere"
id="in-sphere"
title="Sphere"
step="{{user.filter.sphere.step}}"
min="{{user.filter.sphere.min}}"
max="{{user.filter.sphere.max}}"
maxlength="{{user.filter.sphere.maxlength}}"
placeholder="{{user.filter.sphere.placeholder}}"
ng-model="user.input.base.sphere"
select-on-click
sphere>
Question: How can I still let $scope.$watch to be triggered even over a undefined ng-model
You can attach the following function to the ng-keyup directive. This function will get a reference to your form control and read the value that the user has typed in even though the value in the model has not yet been updated.
$scope.changeHandler = function (a) {
var element = angular.element(document.querySelector('#in-sphere'))[0];
console.log(element.value);
}
Attach it to your form control with this:
ng-keyup='changeHandler()'
Here is a working plunker
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}"
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"
/>
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.
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';
}
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';
}
}
});
When validation fails on an input, the model is being set to undefined. Is there a way to prevent this. I'm on Angular version 1.4.8.
E.g.
<input id="name" ng-model="inputname" minlength="4">
When I insert some value here and then reduce it to less than 4 characters the model inputname becomes undefined.
Figured this out, there is an option under ngModelOptions called allowInvalid which allows the modelValue to be updated with the viewValue even if it is invalid (instead of the default behaviour of setting it to undefined).
From the documentation:
allowInvalid: boolean value which indicates that the model can be set with values that did not validate correctly instead of the default behavior of setting the model to undefined.
I'm developing an angular app, where I have to check the form changes, and perform some operation depending upon the changes. I know, this can be achieved using $scope.watch but that would cost me performance.
What I'm trying to achieve
I've a pre-populated form and I will take the form data as a reference using angular.copy() and compare with original data.
I had also tried $parsers to check, but failed. So, is there any way to accomplish this?
You can use ng-change.
From the docs - "Evaluate the given expression when the user changes the input. The expression is evaluated immediately, unlike the JavaScript onchange event which only triggers at the end of a change (usually, when the user leaves the form element or presses the return key)."
Difference between ng-change and $watch
Did you try with ng-change ? It call a function when value of ng-model change
<input type="search" ng-change="changeValue()" ng-model="test" name="test"/>
If you want to know wich field call the function you can add the name as string parameter
<input type="search" ng-change="changeValue('field1')" ng-model="field1" name="field1"/>
In the controller don't forget to put the function changeValue in the scope
$scope.changeValue = function(fieldname){
switch (fieldname){
case 'field1' : .... break;
case 'field2' : .... break;
case 'field3' : .... break;
...
}
}
When you compare the copied object against the original form data, and you want to check for changes, you can use angular.equals.
angular.equals(formObj, formObjCopy)
This will do a deep compare of the object and its nested sub-properties, and return true if the object hasn't changed.
While editing an angular textbox marked as "required", I am unable to delete the 1st letter. However if I move my cursor to before the 1st letter, and enter some text and then delete the last letter left earlier, I am able to do my required edit.
Note: No such issue being observed while creating a new record.
Please find the sample code being used.
<input type="text" class="form-control" ng-model="vm.xyz.firstName" ng-model-options="modelOptions" required />
Used library version: angular: 1.3.15, bootstrap:3.3.2
Thanks in advance.
This is the expected behaviour as per documentation:
If the validity changes to invalid, the model will be set to undefined, unless ngModelOptions.allowInvalid is true.
Since you set your field as required, when it becomes empty, it becomes $invalid, which in turn sets your model to undefined. If you add allowInvalid: true to your ngModelOptions settings, your model will be allowed to receive the empty string value instead of undefined. Plunkr here.
As described by #leonardo-braga, this happens because the field becomes invalid upon deletion of the last character and the $modelValue set to undefined (which tricks your getter/setter into believing it has been called in "getter" mode).
A similar question has been answered on GitHub.
Copying here for easier reference:
Using allowInvalid will work around the issue, but it is not solving the actual problem.
The actual problem lies in the implementation of the getter/setter function, which treats passing an argument with value undefined as passing no argument at all (although these are obviously two very distinct cases).
You should use a more "accurate" way of defining whether the function is called as a getter or as a setter (i.e. whether an argument has been passed (even if undefined) or not).
E.g.:
getterSetterFn: function (newValue) {
if (arguments.length) {
_value = newValue;
}
return _value;
}
See, also, this short demo.