I'm writing a directive that requires ngModel and adds formatters and parsers to manipulate the value. It works great, but since the manipulation depends on external data I have to $watch, I'm looking for a way to update the model value from this watch. I tried to call $setViewValue
but nothing happens (because $viewValue did not change??).
In the following simple example, changes to `factor´ don't update the model value:
angular.module('app', []).directive('multiply', multiplyDirective);
function multiplyDirective() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
factor: '=multiply'
},
link: function(scope, elem, attr, ngModel) {
ngModel.$formatters.push(function (value) {
return value / scope.factor;
});
ngModel.$parsers.push(function (value) {
return value * scope.factor;
});
scope.$watch('factor', function () {
// how to run the parsers pipeline to update modelvalue?
ngModel.$setViewValue(ngModel.$viewValue);
});
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.6/angular.min.js"></script>
<body ng-app ng-init="factor = 1; value = 1">
<input type="number" ng-model="value" multiply="factor" /> x
<input type="number" ng-model="factor" />
= {{ value }}
</body>
Edit: It works if I call the internal $$parseAndValidate
method, but I wonder if there is a public API to enforce the update.
Edit: I figured out, that the behaviour of ngModel.$setViewValue(ngModel.$viewValue);
changed in 1.3.0
. Using 1.2.x
the code works!
A tad late, but I have described how you can do this here. This works on AngularJS 1.6.x
Basically you can trigger the $parsers pipeline
by using $setViewValue()
.
Below is a code snippet that allows you to programmatically set the ngModel to anything you like, by "hijacking" the $parsers pipeline. This is done using a closure.
Set up a $parser with a closure like this:
const hijack = {trigger: false; model: null};
modelCtrl.$parsers.push( val => {
if (hijack.trigger){
hijack.trigger = false;
return hijack.model;
}
else {
// .. do something else ...
})
Then for (re)setting the model you need to trigger the pipeline by changing the $viewValue
with modelCtrl.$setViewValue('newViewValue')
. (Yes it is true that the new view value must be different with respect to the current value).
const $setModelValue = function(model){
// trigger the hijack and pass along your new model
hijack.trigger = true;
hijack.model = model;
// assuming you have some logic in getViewValue to output a viewValue string
modelCtrl.$setViewValue( "some new view value" );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With