Reputation: 3505
I am making a directive that will block render on a input while it is focused.
The use case is that we receive frequent live updates from the backend, and it will overwrite anything the user is writing in an input because of the ng-model binding.
We send the update on pressing enter using ng-keydown="($event.keyCode === 13) ? $ctrl.setItem($event, $ctrl.item) : return"
where $ctrl.setItem will send a request to the backend.
I almost got it working like I want it, only I'm getting surprising results when I blur the input after making an edit.
I made a plunker where I simulate backend updates using $interval: https://plnkr.co/edit/XNFA3bQtbGliAhS0uVmP?p=preview
app.directive('noUpdatesWhenFocused', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (!ngModel || element[0].type !== 'text') {
return;
}
var hasFocus = false;
element.on('focus', function() {
hasFocus = true;
});
element.on('blur', function() {
hasFocus = false;
ngModel.$render();
});
ngModel.$render = function() {
if (!hasFocus) {
element.val(ngModel.$viewValue);
}
};
}
};
});
If you blur after making a edit in the first input field and waiting a few seconds for a new "update", then the model reverts to the state of the last view edit, instead of the last model update. We always want to show the latest update from the backend, so this is what I need to fix, but I am stumped.
Upvotes: 0
Views: 810
Reputation: 3505
Alright I figured it out. The ngModel.$pasers were being fired before the blur event. So I added a parser that ignores the viewValue if it hasn't changed from last time the parsers were called, which it hasn't if you blur.
var app = angular.module('myApp', []);
app.directive('noUpdatesWhenFocused', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (!ngModel || element[0].type !== 'text') {
return;
}
var hasFocus = false;
element.on('focus', function() {
hasFocus = true;
});
element.on('blur', function() {
hasFocus = false;
ngModel.$render();
});
ngModel.$render = function() {
if (!hasFocus) {
element.val(ngModel.$viewValue);
}
};
var vValue = ngModel.$viewValue;
ngModel.$parsers.unshift(function(viewValue) {
var returnValue = viewValue === vValue ? ngModel.$modelValue : viewValue;
ngModel.$setViewValue(returnValue);
ngModel.$render();
vValue = viewValue;
return returnValue;
});
}
};
});
app.run(function($rootScope, $interval) {
$rootScope.item = 'item';
$interval(function(count) {
if (typeof count === 'number') {
if($rootScope.item) {
$rootScope.item = $rootScope.item + '' + count;
}
else {
$rootScope.item = '' + count;
}
console.log($rootScope.item);
}
},3000);
});
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<form name="myForm">
<input
no-updates-when-focused
name="item1"
ng-model="item"
size="50"
required>
</input>
<span ng-show="myForm.item1.$error.required">Required!</span>
</br>
<input
name="item2"
ng-model="item"
size="50"
required>
</input>
<span ng-show="myForm.item2.$error.required">Required!</span>
</form>
</body>
</html>
Upvotes: 1