ng-change triggered before value is updated - angularjs

I have a form which needs to save data each time something is changed. I've used ng-change on all form elements to trigger a form validation and a save. However in case of radio buttons, ng-change is triggered before the actual value is updated, thus resulting in an invalid form on the first try, and an outdated form all subsequent times.
I've set up a JSFiddle to illustrate this. The console prints out whether the form is valid or not. The same applies if I were to print the value of $scope.form.test.$modelValue.
// HTML
<div ng-controller="MyCtrl">
<form name="form">
<input type="radio" name="test" ng-model="test" value="yes" required ng-change="checkRadios()" /> Yes<br/>
<input type="radio" name="test" ng-model="test" value="no" required ng-change="checkRadios()"/> No
</form>
</div>
// JS
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.test = null;
$scope.checkRadios = function(){
console.log($scope.form.test.$modelValue);
}
}
Is my logic faulty, is this a valid bug, or does it work as expected? In the last case, what can I do to always get the actual value?

You need a delay to get the updated value of the $scope.form, so it is possible to achieve by using $timeout
http://jsfiddle.net/loen22/w7dpx57f/
$scope.checkRadios = function(){
$timeout(function () {
console.log($scope.form.$valid);
});
}

Related

Adding numbers in AngularJS

Just started learning AngularJS. I was trying to understand the two-way data binding by writing a code that adds two numbers from the input boxes.
Here's what I have written:
HTML
<div ng-app="cal">
<div ng-controller="calc">
<input type="text" ng-model="a"/>
<input type="text" ng-model="b"/>
<span>{{result}}</span>
</div>
</div>
JavaScript
var app=angular.module("cal", []);
app.controller("calc",function ($scope) {
$scope.result=Number($scope.a || 0)+ Number($scope.b || 0);
});
This, however, doesn't give the intended result i.e "result" is not updated automatically.
But the following code works:
HTML
<div ng-app="cal">
<div ng-controller="calc">
<input type="text" ng-model="a" ng-keyup="add()"/>
<input type="text" ng-model="b" ng-keyup="add()"/>
<span>{{result}}</span>
</div>
</div>
JavaScript
app.controller("calc",function ($scope) {
$scope.add=function(){
$scope.result=Number($scope.a || 0)+ Number($scope.b || 0);
}
});
Why do I need the "keyup" directive? Shouldn't the variable "result" be updated automatically as the values in the fields change?
I also created a jsfiddle here:
https://jsfiddle.net/Deadboy/m9ket0sL/
No, it shouldn't. The controller function is executed once, when angular sees the ng-controller in the DOM. It's not executed every time you type something in the input boxes. So the code computing the result is only executed once, before the user has had a chance to type anything in the inputs.
You don't need the keyup, though (and that wouldn't work if you pasted the value with your mouse, BTW). All you need is
$scope.add = function(){
return (Number($scope.a || 0)+ Number($scope.b || 0));
}
and in the view:
<span>{{ add() }}</span>
When doing it that way, angular will reevaluate the add() expression after each event that modifies the model, and update the DOM with the result of the expression.
If you want to compute the result only when a or b changes, you should be using ng-change, not ng-keyup.
Note that if your inputs are supposed to contain numbers only, you should use an input of type number, not text. In that case, $scope.a and $scope.b will be of type number, not string.
scope variable (a) and (b) are being updated since they are two-way binded.
variable a1 and b1 won't get the update since they only get updated when you initiate your controller "calc" and they are not two-way binded.
To update your result scope variable you can use ng-change to set variable a1 and b1 with updates from $scope.a and $scope.b
<div ng-app="cal">
<div ng-controller="calc">
<input type="text" ng-model="a" ng-change="updateResult()"/>
<input type="text" ng-model="b" ng-change="updateResult()"/>
<span>{{result}}</span>
</div>
/**
* Created by rajat on 12-Nov-16.
*/
var app=angular.module("cal", []);
app.controller("calc",function calc($scope) {
$scope.updateResult = function () {
var a1 = Number($scope.a || 0);
var b1 = Number($scope.b || 0);
$scope.result=a1+b1;
}
});

angularjs object changes affects other object

I have AngularJS application. From server I am getting a JSON. I am assigning to two objects. One I am using in my form page to edit. When the user press submit, I want to check whether the new value and old are same. But whenever I update the value in form and checks, the other object value also gets updated., resulting both object becomes same always.
Here is the JSFiddle Demo. Whenever I am trying to change the value, the other value also gets updated.
What is the solution to prevent the other object to not change?
HTML
<div ng-app>
<h2>Form</h2>
<div ng-controller="MainCtrl">
<form name="myForm" ng-submit="saveForm()">
<input type="text" ng-model="obj.name">
<input type="text" ng-model="obj.value">
<input class="btn-primary" type="submit" value="Save" ng-disabled="myForm.$pristine">
</form>
</div>
</div>
JS
function MainCtrl($scope) {
$scope.obj={
name:"N1",
value:"val"};
var oldObj = $scope.obj;
$scope.saveForm=function() {
alert(JSON.stringify($scope.obj));
alert(JSON.stringify(oldObj));
if($scope.obj===oldObj){
alert("same");
} else {
alert("not same");
}
}
}
The problem is, that you just store an reference to oldObj.
So oldObj and the $scope.obj is always the same.
You need to copy it.
Check the fiddle: http://jsfiddle.net/fc0jo1so/1/
I did it with fromJson, toJson
You just change this line
var oldObj = angular.copy($scope.obj);
Try it will work. & give alert not same
Try using jQuery to make a deep copy
var oldObj = jQuery.extend(true, {}, $scope.obj);

Referencing the element that is calling a controller function Angularjs ( ng-change / ng-blur / ng-* ? )

The original question asked about how to determine which element called the controllers blurr function, but I didn't clarify that I was not specifically asking about ng-blur, but ng-* (ng-change, ng-focus, ng-mouseover, ng-*) in general. So, with that in mind:
How do I determine which element input is calling the blurr() and/or check() functions?
html
<body ng-app="test">
<div ng-controller="Cntrlr as cntrlr">
<form name="meta_test">
<input type="text" name='inpt' ng-model="cntrlr.inpt" ng-blur="cntrlr.blurr()" ng-change="cntrlr.check()" />
<input type="text" name='second' ng-model="cntrlr.second" ng-blur="cntrlr.blurr()" ng-change="cntrlr.check()" />
</form>
</div>
</body>
js
var app = angular.module("test", []);
app.controller("Cntrlr", ["$scope", function($scope){
this.blurr = function(){
alert("which input am I?");
alert("this is so meta.");
// ?
};
this.check = function(){
alert("this is how meta I am:");
alert(this);
}
$scope.Cntrlr = this; // see: (reference)
return $scope.Cntrlr;
}]);
You may be asking yourself "why would he want to do this?"
There are 2 reasons:
because I want to call:
$scope.user_form[meta_test.[(whatever this element is.name)]].$setValidity('spike', false);
because I'm curious. There has to be a simple way to do this.
(reference):
controller as syntax
Use this -
<input type="text" name='inpt' ng-model="cntrlr.inpt" ng-blur="cntrlr.blurr($event)" ng-change="cntrlr.check()" />
This returns the jQuery lite version of the event that causes the blurr function. Once you receive this element in your controller, you can pretty much do whatever you want with it.
The .target attribute of the event will give you the required element.
Should work
Try this:
<form name="meta_test">
<input type="text" name='inpt' ng-model="cntrlr.inpt" ng-blur="cntrlr.blurr()"
ng-change="cntrlr.check('One')" />
<input type="text" name='second' ng-model="cntrlr.second"
ng-blur="cntrlr.blurr()" ng-change="cntrlr.check('Two')" />
</form>
In JS,
this.check = function(Type){
if(Type == "One"){
//Then it is the first text box.
}else if(Type == "Two"){
//Then it is the second text box.
}
}

With AngularJS can I call a function when a user changes a value in a text field?

I have some text fields on my web page. Is there a way that I can call a function when a user changes a value in a text field without using a watch?
Yes. Check out ng-change. It allows you to run a function when an input changes.
<script>
function Controller($scope) {
$scope.counter = 0;
$scope.change = function() {
$scope.counter++;
};
}
</script>
<div ng-controller="Controller">
<input type="text" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
<label for="ng-change-example2">Confirmed</label><br />
<tt>counter = {{counter}}</tt><br/>
</div>
I don't have enough mana to make comment, so - for delaying fire action you can use ng-model-options directive and debounce
Details in documentation: https://docs.angularjs.org/api/ng/directive/ngModelOptions

Configure Angular to re-evaluate on blur instead of keypress

By default, it seems that Angular reevaluates its binding from a particular DOM element (e.g Text Input) to the underlying scope property on keypress or paste - i.e, whenever the value in the text input changes.
Is it possible to make it only refresh the binding on blur? I.e. do something like:
<div ng-app>
<div ng-controller="ctrl">
<input type="text" ng-model="base" ng-update-type="blur"/>
<input type="text" />
<span ng-bind="doubled()" />
</div>
</div>
Take the following JS fiddle:
http://jsfiddle.net/f76dW/
I would like the doubled span to only update when I move the focus out of the first input
You can use ng-blur and a dummy variable (base_ in this case) to achieve that effect: http://jsfiddle.net/f76dW/1/
Template
<input type="text" ng-model="base_" ng-blur="updateBase()" />
Controller
function ctrl($scope) {
$scope.base = $scope.base_ = 1000;
$scope.updateBase = function () {
$scope.base = $scope.base_;
};
$scope.doubled = function() {
return $scope.base * 2;
}
}
Use ng-model options. Using a blur hack is tricky, because a blur may not be a change.
<input type="text" ng-model="a.b" ng-change="callScriptThenServer()" ng-model-options={updateOn: 'blur'}"/>

Resources