从$ watch触发ngModelController $解析器管道

时间:2014-12-19 12:02:22

标签: javascript angularjs angularjs-directive angularjs-ng-model

我正在编写一个需要ngModel的指令,并添加格式化程序和解析器来操作该值。它工作得很好,但由于操作依赖于外部数据,我必须观看,我正在寻找一种方法来更新这款手表的模型值。我试着调用$setViewValue但没有任何反应(因为$ viewValue没有改变??)。

在下面的简单示例中,对“factor”的更改不会更新模型值:

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>

编辑:如果我调用内部$$parseAndValidate方法,它会起作用,但我想知道是否有公共API来强制执行更新。

修改:我发现ngModel.$setViewValue(ngModel.$viewValue);1.3.0的行为发生了变化。使用1.2.x代码可以使用!

3 个答案:

答案 0 :(得分:1)

有点晚了,但我已经描述了你如何做到这一点here。这适用于AngularJS 1.6.x

基本上,您可以使用$parsers pipeline触发$setViewValue()

下面是一个代码片段,允许您通过“劫持”$ parsers管道以编程方式将ngModel设置为您喜欢的任何内容。这是使用闭包来完成的。

使用这样的闭包设置$解析器:

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

然后,为了(重新)设置模型,您需要通过$viewValue更改modelCtrl.$setViewValue('newViewValue')来触发管道。 (是的,新视图值必须与当前值不同)。

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 :(得分:0)

根本没有加载你的指令。您需要将模块名称提供给ng-app,如<body ng-app="app">。然后,只要修改了因子,就可以调用ngModel.$setViewValue(ngModel.$viewValue)

&#13;
&#13;
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);
             
            });
        }
    }
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

从v1.3.18的来源,我看不到公开的api。管道来自 $$ parseAndValidate 方法,该方法也是从 $ commitViewValue 调用的(需要提交/设置不同的值,否则它将return