【问题标题】:Dynamically add $watch动态添加 $watch
【发布时间】:2016-01-23 12:10:43
【问题描述】:

我有一个 JSON 数据集,其中每一行都包含一个输入值、一小块用于计算的 JavaScript 代码和一个小计值,该小计值是 eval() 执行计算后输入值的结果。数据集将包含一行或多行。每个输入都将以 HTML 格式重复到页面上,并且小计的总和将作为总值以及每个单独的小计值显示给用户。

我尝试使用 $watch 并为转发器中的每一行添加一个,但当用户更改输入值时,我似乎无法让它们触发。

我创建了我的第一个Sample_Plunker 来展示我想要实现的目标,但没有成功。

不确定我是否也应该在此处发布代码,但我们将不胜感激。

基本上是我的 HTML:

<div ng-controller="MainCtrl as MainCtrl" ng-init="MainCtrl.init()">
    <div ng-repeat="rule in MainCtrl.myCode">

        Input_{{$index}}: <input ng-model="rule.inpValue" type="number" />
        <!-- the following needs to reflect the result after eval() of myCode[?].code from JSON below -->
        Subtotal: {{rule.nSubTotal}}
        <br />
    </div>
    <br />

    <!-- the following should be a sum of all values above -->
    Total: {{MainCtrl.nTotal}}

</div>

这是我的示例数据和没有响应的 $watch:

app.controller('MainCtrl', function($scope) {
    var _this = this;
    _this.nTotal = 0;
    // sample js code tht will be execuited later
    _this.myCode = [{
        "code": "_this.myCode.inpValue +2",
        "inpValue": 0,
        "nSubTotal": 0
    }, {
        "code": "_this.myCode.inpValue*3",
        "inpValue": 0,
        "nSubTotal": 0
    }, {
        "code": "_this.myCode.inpValue/5",
        "inpValue": 0,
        "nSubTotal": 0
    }];

    this.init = function() {
        $scope.$watch('MainCtrl.myCode[i].inpValue', function() {
            // debugger;

            // assuming if watch would fire, subtotal = eval( input ) 
            _this.nSubTotal = eval(_this.myCode[i].code);

            // I would also keep a running total at this point
            _this.nTotal = _this.nTotal + _this.myCode[i].nSubTotal;
        });
    }; //end init()
});

【问题讨论】:

  • 您的手表没有触发,因为$scope 上没有属性i 正在评估手表表达式。你能解释更多关于数据集的信息吗?为什么需要那些 JS 代码 sn-ps?据我所知,它们甚至无效。 _this.myCode.inpValue 将解析为 undefined,因为 _this.myCode 是一个数组。
  • 没有通配符[i] 可以让您的手表正常工作。此外,您在手表内部的逻辑也不会正确跟踪总数。像这样使用eval 看起来真的很危险,而且真的没有多大意义。有更简洁的方法可以使用 2 个属性而不是 eval 来进行修饰符
  • Igor:代码 sn-ps 是购物车系统的一部分,但与我以前见过的任何东西都不一样。有些房东是一个相当固定的群体,大约 800 人(将这些视为公司)。每个主机都有 1 个或多个适用于其客户的结帐规则。每台主机大约有 150 个客户。主机可以创建规则(代码 sn-ps)并将它们分配给他们的每个客户。所以要将所有这些规则硬编码到源代码中,我认为从数据中获取规则是处理这种大小的矩阵的更好方法。为了维护它,我只需要修改数据。

标签: angularjs angularjs-watch


【解决方案1】:

您的代码有很多错误,但基本上要回答您的问题,您可以通过循环遍历数组并为每个元素调用一次 $scope.$watch 来对数组中的每个项目应用监视。

我还强烈建议您使用实际函数而不是 eval() 来评估表达式。

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {

  var _this = this;
  _this.nTotal = 0;

  // sample js code tht will be execuited later
  _this.myCode = [{
    "code": function() { return this.inpValue + 2; },
    "inpValue": 0,
    "nSubTotal": 0
  }, {
    "code": function() { return this.inpValue * 3; },
    "inpValue": 0,
    "nSubTotal": 0
  }, {
    "code": function() { return this.inpValue / 5; },
    "inpValue": 0,
    "nSubTotal": 0
  }];

  function sum (values) {
      return values.reduce(function (a, b) { return a + b; }, 0);
  }

  this.init = function() {

    _this.myCode.forEach(function(code) {
      $scope.$watch(function() {
        return code.inpValue;
      }, function() {
        code.nSubTotal = code.code();
        _this.nTotal = sum(_this.myCode.map(function(c) { return c.nSubTotal; }));
      });
    });

  };

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js" data-semver="1.4.7" data-require="angular.js@1.4.x"></script>
<div ng-app="plunker" ng-controller="MainCtrl as MainCtrl" ng-init="MainCtrl.init()">
  <div ng-repeat="rule in MainCtrl.myCode">

    Input_{{$index}}:
    <input ng-model="rule.inpValue" type="number" />

    Subtotal: {{rule.nSubTotal}}
    <br />

  </div>
  <br />

  <!-- the following should be a sum of all values above -->
  Total: {{MainCtrl.nTotal}}

  <br />
  <br />

</div>

【讨论】:

  • eval() 有什么问题?我在想 eval 会被 try/catch 包裹起来。存储在 JSON 中的代码在上线之前就已经过预测试。您的回答为我提供了很多新东西,从未使用过 map 或 reduce - 非常酷!而且我仍在尝试了解您所做的事情,但它确实看起来简单而直接。 太棒了 - 谢谢!
【解决方案2】:

您的$watch 没有触发,因为您正在观看一个表达式,该表达式将始终针对$scope 评估为undefined。在查看一组项目以进行更改时,您有几种选择:

  1. 每个项目都有一个单独的$watch。效率不高,尤其是在数组很大的情况下。
  2. 一个$watchGroup,它接受一个监视表达式数组。当表达式不统一时很有用,例如不同对象的不同属性。
  3. $watchCollection,它浅层监视单个对象。我认为这最适合您的情况。

另外,请注意$watch* 的第一个参数可以是一个函数,它会返回一个您想要查看的值。把这些放在一起,你的观察者看起来像

$scope.$watchCollection(function() {
  var aInputs = [];
  for (var i = 0, len = myCode.length; i < len; ++i) {
    aInputs.push(myCode[i].inpValue);
  }
  return aInputs;
}, function() {
  // one of the inpValues in myCode has changed
  // need to re-compute nTotal
});

另外,我强烈建议您不要使用eval(),并遵循 JLRishe 的使用函数的建议。如果您绝对需要评估字符串表达式,您可以使用 Angular 的$scope.$eval 安全地执行此操作。这将针对范围和一组局部变量评估表达式。例如,

$scope.$eval("inpValue + 2", { inpValue: 3 }) === 5;

这是一个有效的Plunker。它使用$eval 作为概念证明,但同样,如果可以,请使用普通函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    • 2014-02-22
    • 1970-01-01
    • 2021-11-11
    • 2012-03-14
    相关资源
    最近更新 更多