AngularJS directive not being trigged on manual change to a field value - angularjs

I've got an input field and a button that manually updates the input field with a value. The field also has a directive that monitors changes for error handling.
When typing into the input field, the directive triggers just fine. When the button is pressed to manually update the input field, the directive does not get triggered, and I can't figure out why. This is causing my validation to not update, sometimes causing an error to persist when the value is actually not an error.
Here is the input field in question:
<input testchange type="text" name="hello" ng-model="formData.hello"/>
Here is the button to update the value:
update text
Here is the directive:
angular.module('app').directive('testchange', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
console.log('value changed to ' + viewValue);
scope.history.push(viewValue);
})
}
}
});
I have made a simple example of the problem: http://jsfiddle.net/gakman/7zF9j/5/

You need to use a formatter in the case when you are updating the model value from button click. Understand your are updating model now not the view when you set formData.hello='hello world'.
You need to create a formatter and push it into $formatters collection on ngModelController. See documentation http://docs.angularjs.org/api/ng/type/ngModel.NgModelController
See my updated fiddle http://jsfiddle.net/cmyworld/N57JM/

Related

ngModel updated after ngBlur

I'm using ui-bootstrap's datepicker input with AngularJS. It has an ngModel and a validatioon method bound to ngBlur event:
<input id="startDate" type="text" ng-model="myCtrl.startDate" ng-blur="myCtrl.validateDate()"/>
The validation method checks the ngModel according to set of rules in the controller, and alerts if needed. Here is the related part of the controller:
var vm = this;
vm.startDate = new Date();
vm.minStartDate = new Date(2000,1,1);
vm.validateDate = function(){
if(vm.startDate < vm.minStartDate)
alert("Start date can't be earliar than 2000");
}
The problem is that, the validation method runs before the update of the model. Example scenario is as follows:
The rule: startDate can't be earlier than year 2000.
When the page loads, startDate value is initially set to today. The user selects 31.12.1999 from the calendar, ngBlur method is called. However, ngModel is not yet updated. Thus the validation checks today's value and says ok. Actually, it should have checked ngModel's value with 31.12.1999. The value changes simultaneously on the input view, but in the background, it is set after the blur method is fired. As far as I know, two-way binding in AngularJS normally serves for this purpose but the ngBlur must be an exception.
I have tried many thing such as;
Adding ng-model-options to the component:
ng-model-options="{ updateOn: 'blur' }"
Using pre and post linking by modifying the input element
Using ng-change instead of ng-blur
However, none of the above works, and still ng-blur runs before the model is updated.
I have found the solution by manually updating the values inside the controller. Obviously AngularJS won't update the model with the value inside the HMTL element until blur event is fired. So, it's better to write a method to update it.
I can access the value inside the element as follows:
vm.startDate = angular.element('#startDate').val();
Then I can wrap this with a method:
vm.updateModels() = function(){
vm.startDate = angular.element('#startDate').val();
}
Finally, I just call this at the very first of my blur event:
vm.validateDate = function(){
//Update first
vm.updateModels();
//Then validate
if(vm.startDate < vm.minStartDate)
alert("Start date can't be earliar than 2000");
}
Ta tammm, now I can validate the updatet values!
A simple way to make sure that your validation fires only once the value is updated would be to call it on an ng-change instead of ng-blur. That way you are sure that the controller evaluates only once the ng-model value has been updated.
Try to build you own custom validator (directive)
app.directive('integer', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.integer = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
// consider empty models to be valid
return true;
}
if (INTEGER_REGEXP.test(viewValue)) {
// it is valid
return true;
}
// it is invalid
return false;
};
}
};
});
Check this example
https://plnkr.co/edit/SZoBKakhzIq5wRoCvEyG?p=preview

setViewValue in directive on input not updating actual visible input value

I've been fighting with this for almost two days. I hope you guys can help me.
Summary:
I have problems setting the view value of some input fields programatically.
I have a form with inputs whose values are saved before the form is removed (multiple elements and multiple forms possible, user might close a form, and reopen later). On reopening the form I want to restore the previous view values (main reason is to get back also the invalid view values which were not saved in the model). This doesn't work.
If I call ctrl.$setViewValue(previousValue) I get the model (visibly) updated (if valid), the view values of the formControl (while debugging in console) are changed too, but I don't get them actually rendered in the input fields. I don't understand why :(
I reduced the problem to this fiddle:
http://jsfiddle.net/g0mjk750/1/
javascript
var app = angular.module('App', [])
function Controller($scope) {
$scope.form = {
userContent: 'initial content'
}
}
app.controller('Controller', Controller);
app.directive('resetOnBlur', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind('blur', function () {
console.log(ngModel);
scope.$apply(setAnotherValue);
});
function setAnotherValue() {
ngModel.$setViewValue("I'm a new value of the model. I've been set using the setViewValue method");
}
}
};
});
Html
<form name="myForm" ng-app="App" ng-controller="Controller" class="form">
Text: {{form.userContent}}
<hr />
If you remove the text, "Required!" will be displayed.<br/>
If you change the input value, the text will update.<br/>
If you blur, the text will update, but the (visible) input value not.
<hr />
<input class="input" type="text" ng-model="form.userContent" name="userContent" reset-on-blur required></textarea>
<span ng-show="myForm.userContent.$error.required">Required!</span>
</form>
I hope you guys can explain to me why this doesn't work and how to fix this...
You need to call ngModel.$render() to have the viewvalue change reflected in the input. There is no watch created on $viewValue so that changes are automatically reflected.
function setAnotherValue() {
ngModel.$setViewValue("I'm a new value of the model. I've been set using the setViewValue method");
ngModel.$render();
}
Plnkr
Default implementation of $render does this:-
element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
However you can override and customize your implementation for $render as well..
try scope.$apply() to invoke change on model since you're liking changing model outside of scope where ngModel was inited

How to update an input's value after calling $setViewValue

I have a form input with a ng-model as well as my custom directive which reads cookie data and should set the input value:
<form>
<input type="text" id="name" ng-model="name" my-cookie-directive>
</form>
My directive:
angular.module('myApp.directives').directive('myCookieDirective', ['CookieService', function(CookieService) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var cookieVal = CookieService.getCookie(attrs.ngModel);
if(cookieVal != '') {
ctrl.$setViewValue(cookieVal);
elem.val(cookieVal); //not very cool hum?
}
}
};
}]);
When logging ctrl.$modelValue I can see that the right cookie data was set to my controller variable name but the input stays blank. I know that $setViewValue does not trigger a $digest and therefore tried ctrl.$render() but nothing happens.
I ended up using jQuery to set the input's value which is not satisfying at all.
Thanks in advance :)
You are correct in not wanting to use jQuery to set the input's value. Why would you be using Angular if you are going to do that then?
You can see a new Plunker here, using a different approach to the ones being mentioned. My suggestion: use NgModelController when you want to handle validations and format the model value.
For your current situation, you can use an isolated scope in the directive, and pass to it the scope property you want to update with the cookie value. Then in the directive, you can simply do:
scope.cookieField = cookieVal;
And Angular will handle the data binding and update the view value to match the model value. Plus, this is completely reusable.
Use $render and wrap everything in a function passed to $evalAsync:
if(cookieVal !== '') {
scope.$evalAsync(function(){
ctrl.$setViewValue(cookieVal);
ctrl.$render();
});
}
Plunker demo

Need to re-render view and re-initialise controller in AngularJS

I have a form that I want to reset. If I use the $setPristine method in Angular, this gets me some of the way.
However, if you look at the following isolated case, you will see that when you press the reset button to reset the form, a save message will be logged to the console. This is where I would write the code to save any changes made to the form to the server.
http://jsbin.com/OLIziRe/14/edit
In this example, save is logged when the value is different. This includes when the form is set to pristine, as the previous value will be whatever it was before the form was reset, and the new value will be undefined.
I do not want this behaviour. I only want to log the save message if there are changes — if the form is reset, there should be no "previous value".
The only solution I can come up with is to do some trickery in order to re-render the view and re-initialise the controller, thus losing all previous state.
http://jsbin.com/OLIziRe/12/edit
What would be the AngularJS way to do this?
You can do just like that with your first solution :
var app = angular.module('myApp', []);
app.directive('someDirective', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
scope.$watch(function () {
return ctrl.$modelValue;
}, function (newValue, lastValue) {
// Only save if the values do not match
if(scope.someForm.$dirty){
console.log(newValue !== lastValue ? 'save' : '');
}
});
}
}
});
Before the console.log you check if the form is in $dirty state, i.e if the user have set a value to the form.
When you do a $setPristine() you reset the form in $pristine state so the $dirty state is false.
Hope it helps !

How do I update the scope/ng-model when a third party component modifies the textbox value?

I want to make an editor with using color picker.
Here is a simplified sample.
http://jsfiddle.net/xcUev/8/
I'm treating the color as the attribute of angular scope object.
I made it choosable by using color picker
http://www.html5.jp/library/cpick.html
but after choosing the color, It wouldn't affect as the scope.
Do you have any ideas for making it works like how to affect to scope this input state intentionally.
Please help me...
I tried bind()ing a change event, but it never fired (probably because the color picker stops propagation of the event). So I next tried bind()ing a focus event, and that seems to work (I noticed the textbox lost focus, then got it back again when the canvas was hiding).
HTML:
<input type="text" ng-model="data.color" class="html5jp-cpick" cpick>
Directive:
app.directive('cpick', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
element.bind('focus', function() {
ngModelCtrl.$setViewValue(element.val());
scope.$apply();
});
}
}
})
Fiddle

Resources