AngularJS - Blur + Changed?
Solution 1
This does what I want. It stores the value on focus, and compares it to the new value on blur, if changed, it triggers the expression in the attribute.
app.directive('changeOnBlur', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ngModelCtrl) {
if (attrs.type === 'radio' || attrs.type === 'checkbox')
return;
var expressionToCall = attrs.changeOnBlur;
var oldValue = null;
elm.bind('focus',function() {
scope.$apply(function() {
oldValue = elm.val();
console.log(oldValue);
});
})
elm.bind('blur', function() {
scope.$apply(function() {
var newValue = elm.val();
console.log(newValue);
if (newValue !== oldValue){
scope.$eval(expressionToCall);
}
//alert('changed ' + oldValue);
});
});
}
};
});
usage:
<input ng-model="foo" change-on-blur="someFunc()" />
Solution 2
Use ng-model options
.
Like this, ng-change
will only trigger when the input is blurred.
<input type="text"
ng-model="a.b"
ng-model-options="{updateOn: 'blur'}"
ng-change="onchange()"/>
Solution 3
How about this solution. Works for me:
<input ng-init="oldValue = value" ng-model="value"
ng-blur="oldValue != value && callYourFunc(foo)">
Solution 4
I am using AngularJs 1.2.x and stumbled upon the ng-change issue of firing on each change. ng-blur can be used but it fires even though there is no change in the value. So both cannot be used efficiently.
With Angularjs 1.3.x, things are easier using ng-model-options
like below
to invoke change function "onBlur"
ng-change="ctrl.onchange()" ng-model-options="{updateOn: 'blur'}"
And
to delay invocation of change function by 500ms
ng-change="ctrl.onchange()" ng-model-options='{ debounce: 500 }'"
Now coming to back to the question of getting such things with AngularJs 1.2.x
to invoke change function "onBlur"
html
<input type="text" ng-model="ctrl.a.c" sd-change-on-blur="ctrl.onchange()" />
or
<input type="text" ng-model="ctrl.a.c" sd-change-on-blur="ctrl.onchange(ctrl.a.c)" />
JS
app.directive('sdChangeOnBlur', function() {
return {
restrict: 'A',
scope: {
sdChangeOnBlur: '&'
},
link: function(scope, elm, attrs) {
if (attrs.type === 'radio' || attrs.type === 'checkbox')
return;
var parameters = getParameters(attrs.sdChangeOnBlur);
var oldValue = null;
elm.bind('focus', function() {
scope.$apply(function() {
oldValue = elm.val();
});
})
elm.bind('blur', function() {
scope.$apply(function() {
if (elm.val() != oldValue) {
var params = {};
if (parameters && parameters.length > 0) {
for (var n = 0; n < parameters.length; n++) {
params[parameters[n]] = scope.$parent.$eval(parameters[n]);
}
} else {
params = null;
}
if (params == null) {
scope.sdChangeOnBlur();
} else {
scope.sdChangeOnBlur(params)
}
}
});
});
}
};
});
function getParameters(functionStr) {
var paramStr = functionStr.slice(functionStr.indexOf('(') + 1, functionStr.indexOf(')'));
var params;
if (paramStr) {
params = paramStr.split(",");
}
var paramsT = [];
for (var n = 0; params && n < params.length; n++) {
paramsT.push(params[n].trim());
}
return paramsT;
}
to delay invocation of change function by 500ms
html
<input type="text" ng-model="name" sd-change="onChange(name)" sd-change-delay="300"/>
OR
<input type="text" ng-model="name" sd-change="onChange()" sd-change-delay="300"/>
JS
app.directive('sdChange', ['$timeout',
function($timeout) {
return {
restrict: 'A',
scope: {
sdChange: '&',
sdChangeDelay: '@' //optional
},
link: function(scope, elm, attr) {
if (attr.type === 'radio' || attr.type === 'checkbox') {
return;
}
if (!scope.sdChangeDelay) {
scope.sdChangeDelay = 500; //defauld delay
}
var parameters = getParameters(attr.sdChange);
var delayTimer;
elm.bind('keydown keypress', function() {
if (delayTimer !== null) {
$timeout.cancel(delayTimer);
}
delayTimer = $timeout(function() {
var params = {};
if (parameters && parameters.length > 0) {
for (var n = 0; n < parameters.length; n++) {
params[parameters[n]] = scope.$parent.$eval(parameters[n]);
}
} else {
params = null;
}
if (params == null) {
scope.sdChange();
} else {
scope.sdChange(params)
}
delayTimer = null;
}, scope.sdChangeDelay);
scope.$on(
"$destroy",
function(event) {
$timeout.cancel(delayTimer);
console.log("Destroyed");
}
);
});
}
};
}
]);
function getParameters(functionStr) {
var paramStr = functionStr.slice(functionStr.indexOf('(') + 1, functionStr.indexOf(')'));
var params;
if (paramStr) {
params = paramStr.split(",");
}
var paramsT = [];
for (var n = 0; params && n < params.length; n++) {
paramsT.push(params[n].trim());
}
return paramsT;
}
plnkrs for both approaches are
http://plnkr.co/edit/r5t0KwMtNeOhgnaidKhS?p=preview
http://plnkr.co/edit/9PGbYGCDCtB52G8bJkjx?p=info
Solution 5
how about this plunkr?
using angular's built in ng-blur
, update your "persisted value" on blur
<input type="text" ng-model="boxValue" ng-blur="doneEditing(boxValue)">
when saving, verify the value is different
$scope.doneEditing = function(v) {
if (v !== $scope.persistedValue) // only save when value is different
$scope.persistedValue=v;
}
There is no special option on ng-blur
for pre-checking equality that I'm aware of. A simple if
seems to do the trick
Related videos on Youtube
Comments
-
Roger Johansson over 2 years
What is the easiest way to combine ng-changed and ng-blur?
I've found this post: How to let ng-model not update immediately?
However, this does no longer work in angluar 1.2+ Is there any way to achieve the same behavior?
I guess I have to store a copy of the old value myself and compare the new value to that on blur if I try to do the same, or is there any easier way ?
-
Chris Montgomery over 10 yearswhat kind of behavior are you trying to accomplish? a plunkr would be very helpful here
-
Roger Johansson over 10 yearsI don't wan't to trigger the event on each key press, I want to trigger the event on blur, but I don't want to trigger if the value hasn't changed.. KnockoutJS has options for this, Angular seems a bit trigger happy on changed events IMO
-
charlietfl over 10 yearsstill not clear what event you are talking about or what higher level issue is
-
Roger Johansson over 10 yearsThere is a now not working example of it here: jsfiddle.net/carpasse/JYTUB/3 this demo does update the label on each keypress which it isn't supposed to do, the ngModelOnBlur did take care of that in earlier versions of angular
-
KayakDave over 10 yearsFYI- Looks like this feature is targeted for Angular 1.2.5: github.com/angular/angular.js/pull/2129
-
-
Yogesh Manware over 9 yearsng-change executes all the time!
-
Niels Steenbeek over 9 years@MYN what is 'all the time!'? Every key-down or every blur or ... See documentation here: docs.angularjs.org/api/ng/directive/ngModelOptions Here is a working example
-
Yogesh Manware over 9 yearsI realized my mistake. I am using 1.2.X version and ng-model-options is introduced in 1.3. it works fine with 1.3. Thanks.
-
Derek over 8 years
ng
prefix should be reserved to native angular directives. -
Derek over 8 yearsOriginal but doesn't work at the 2nd edit/focuson of the input element or if the model is changed from the controller.
-
deadend over 4 years@Niels Steenbeek I type ABCD in text its calling onchange function, then i am delete D and then again type D its again calling onchange function
-
Anoop about 4 yearsThis is really sleek implementation.