NgModelController $ parsers pipe trigger from $ watch

I am writing a directive that requires ngModel and adds formatters and parsers to manipulate the value. It works fine, but since the manipulation depends on external data, I have to look at the clock, I am looking for a way to update the model value from this clock. I tried calling $setViewValue but nothing happens (because $ viewValue hasn't changed?).

In the following simple example, changes to "factor" do not 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 $$parseAndValidate internal method, but I wonder if there is a public API to provide the update.

Edit: I realized that the behavior of ngModel.$setViewValue(ngModel.$viewValue); changed in 1.3.0 . Using 1.2.x , the code works!

+5
source share
3 answers

Late, but I described how you can do it here . This works on AngularJS 1.6.x

Basically, you can run $parsers pipeline with $setViewValue() .

Below is a snippet of code that allows you to programmatically install ngModel on everything you like, by "grabbing" the $ parsers pipeline. This is done using closure.

Configure $ parser with closure as follows:

 const hijack = {trigger: false; model: null}; modelCtrl.$parsers.push( val => { if (hijack.trigger){ hijack.trigger = false; return hijack.model; } else { // .. do something else ... }) 

Then, to (re) install the model, you need to start the pipeline by changing $viewValue to modelCtrl.$setViewValue('newViewValue') . (Yes, it is true that the new view value must be different from 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" ); } 
+1
source

Your directive does not load at all. You need to specify the module name for ng-app, like <body ng-app="app"> . And after that, just call ngModel.$setViewValue(ngModel.$viewValue) whenever the factor changes.

 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.2.23/angular.min.js"></script> <body ng-app="app" ng-init="factor = 1; value = 1"> <input type="number" ng-model="value" multiply="factor" /> x <input type="number" ng-model="factor" /> = {{ value }} </body> 
0
source

From sources v1.3.18 I do not see a public api. The pipeline comes from the $$ parseAndValidate method, which is also called from $ commitViewValue (you need to commit / set a different value or it will return )

0
source

Source: https://habr.com/ru/post/1209540/


All Articles