【问题标题】:Trigger ngModelController $parsers pipeline from a $watch从 $watch 触发 ngModelController $parsers 管道
【发布时间】:2014-12-19 12:02:21
【问题描述】:

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

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

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 代码有效!

【问题讨论】:

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


    【解决方案1】:

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

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

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

    使用这样的闭包设置 $parser:

    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" ); 
        }
    

    【讨论】:

      【解决方案2】:

      您的指令根本没有加载。您需要将模块名称提供给 ng-app,例如 &lt;body ng-app="app"&gt;。之后,只要修改因子,只需调用 ngModel.$setViewValue(ngModel.$viewValue)

      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>

      【讨论】:

      • 我认为 'app' 是默认的 ngApp 参数,所以指令确实加载了。但我注意到,行为在 1.3.0 中发生了变化。带有 $setViewValue 的代码实际上在 1.2.23 中有效,但在 1.3.0 中无效!
      • 您的原始帖子使用的是 Angular 1.2.23,其中 'app' 不是默认的 ngApp。我的答案仅基于 v1.2.23,所以我可以获得“已接受答案”或投票吗?其次,对于 1.3.x,'app' 是默认的 ngApp。显然 $setViewValue 有所改变。我可能对此一无所知,但为什么不创建另一个范围变量来存储计算结果呢?除非你只是想玩ngModelController :>
      • 是的,我很抱歉。我没想到这是一个版本问题。只是将版本 SO 插入到 sn-p 中。但我不能接受这个答案,因为它不能解决我在 1.3.x 中遇到的实际问题。
      • 好的,明白了。但我宁愿拥有另一个范围变量而不是value 来保留计算结果。非常直接,以免弄乱 ngModelController。
      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2021-11-08
        • 2020-06-14
        • 1970-01-01
        • 2022-11-14
        • 2021-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-11
        相关资源
        最近更新 更多