Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger ngModelController $parsers pipeline from a $watch

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!

like image 223
hansmaad Avatar asked Nov 10 '22 21:11

hansmaad


1 Answers

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" ); 
    }
like image 125
Maurits Moeys Avatar answered Nov 14 '22 23:11

Maurits Moeys