$scope.$watch is not triggered when ng-model is undefined? - javascript

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

Related

Using <input oninput= ... with current value in textbox

I have an simple HTML input tag inside my React component, defined thusly:
<input type="text" onInput={(value) => {
search(value);
return value;
}}/>
By using oninput, I am hoping to invoke the search method, with the current value in the textbox as an argument, whenever the value in in the textbox is changed. The search method is defined in the same component thusly:
const search = _.debounce((value) => {
doSomething(value);
}, 500);
When I debug in Chrome Dev Tools, I see that value has the following value, instead of the current value in the textbox:
[[Handler]]: Object
[[Target]] : SyntheticEvent
[[IsRevoked]] : false
What am I doing wrong? How can I call search and provide it the current value in the textbox as an argument? Online examples show oninput being used like <input type="text" oninput="myFunction()">, but they don't show how to pass the value in your textbox as input to the function called on the oninput event.
The parameter of your onInput function is a JavaScript Event object. To extract the value of it, you have to first bind the value of the input box to something:
<input
type="text"
value={this.state.value}
onInput={this.handleSearch}
/>
And then in your component methods, extract this value with event.target, which allows you to look at the DOM elements properties (we want the value property):
handleSearch(event) {
this.setState({value: event.target.value});
this.search(event.target.value); // Or wherever your search method is
}
EDIT: renamed search => handleSearch to avoid confusion :)

Changing `ng-required` calls `ng-change`

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';
}
}
});

Watch ng-model whilst using minlength?

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.

AngularJS : Detect form changes

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.

How to force validation to run on model when other model changes?

I have an input field with a number of $validators registered on it that updates the model.
Some of these validators does comparisons to other values on the scope (which are also updated with input fields).
How do I force AngularJS to run these validations again when the other values are changed on which it is dependant?
I've tried finding anything relating to this in the documentation and also created a $watch on the dependant field and just set the model value to itself (hoping it would force a revalidation) but no luck on either counts.
If you're using Angularjs 1.3+ you can use the $validate method.
Lets say your input "A" is the one which depends on the others inputs, lets call them "B"s.
You can add a function to each of the B's $viewChangeListeners which will just call the A's $validate method. This will have the following effect; each time you modify one of the B input, your A inputs $validators will run.
I know that this was answered a while ago, but I had a similar issue and I managed to cobble a suitable solution together from a stack of other answers and a bit of trial and error. I figure someone else might look for something similar one day...
Here's a method which (as far as I can tell ) ties directly in with the validation system. This particular example creates a match validation rule, which compares two models and validates if their value is identical.
<script type="text/javascript">
angular.module( "YourModule", [] )
.directive( "match", function() {
return {
require: 'ngModel',
restrict: 'A',
link: function( $scope, $elem, $attrs, $ctrl ) {
// targetModel is the name of the model you want to
// compare against.
var targetModel = $attrs.match;
// Add the 'match' validation method
$ctrl.$validators.match = function( modelValue, viewValue ) {
valid = $scope.$eval( targetModel ) == viewValue;
$ctrl.$setValidity( 'match', valid );
return valid ? viewValue : undefined;
};
// When the target model's value changes, cause this model
// to revalidate
$scope.$watch( targetModel, function() {
$ctrl.$validate();
} );
}
};
} );
Then use like this (obviously including a form, ng-app and ng-controller):
<input type="password" ng-model="password" />
<input type="password" ng-model="confirmation" match="password" />
The general idea is that when the match directive is processed, an additional function (match) is added to the $validators on the $ctrl object, which is where other validators (required field, min length, ...) seem to live. This sets up the validation on the field, but that rule is only processed when the field with the match directive is updated.
To combat this, a watch is then set up on the target model. When the value of the target model is updated, it will run the $validate() method on the original control, allowing validation to occur in either of the two fields.

Categories