【问题标题】:AngularJS : watching a particular property in an array of objects for changes in the property's valueAngularJS:观察对象数组中的特定属性以了解属性值的变化
【发布时间】:2014-12-20 14:24:33
【问题描述】:

在我的自定义指令中,我根据数据源数组中的对象数量向 DOM 添加元素。我需要观察每个对象中的特定属性。当我将这些元素添加到 DOM 时,我想在 toppings 数组中每个对象的 checked 属性上设置一个 $watch,但它不起作用,我不知道为什么。我在函数内部设置了一个断点,当属性从 true 变为 false 或 false 变为 true 时应该调用该断点,但该函数从未被调用。原因明显吗?我只是在学习 Angular,所以我很容易犯一个愚蠢的错误。

$scope.bits = 66;  (i.e. onions and olives)


$scope.toppings = [
    { topping: 1, bits: 2, name: 'onions' },
    { topping: 2, bits: 4, name: 'mushrooms' },
    { topping: 3, bits: 8, name: 'peppers' },
    { topping: 4, bits: 16, name: 'anchovies' },
    { topping: 5, bits: 32, name: 'artichokes' },
    { topping: 6, bits: 64, name: 'olives' },
    { topping: 7, bits: 128, name: 'sausage' },
    { topping: 8, bits: 256, name: 'pepperoni' }
     ]

模型中的每个对象都有一个新的checked 属性,该属性为真或假。

注意:对象数组最多包含十几个项目。性能不是问题。

link: function link(scope, iElement, iAttrs, controller, transcludeFn) {


  <snip>

 // At this point  scope.model refers to $scope.toppings. Confirmed.

  angular.forEach(scope.model, function (value, key) {

     // bitwise: set checked to true|false based on scope.bits and topping.bits

     scope.model[key].checked = ((value.bits & scope.bits) > 0);  


     scope.$watch(scope.model[key].checked, function () {
         var totlBits = 0;
         for (var i = 0; i < scope.model.length; i++) {
            if (scope.model[i].checked) totlBits += scope.model[i].bits;
         }
          scope.bits = totlBits;
      });

   });

     <snip>

【问题讨论】:

    标签: angularjs angularjs-directive watch


    【解决方案1】:

    对象数组:

    $scope.toppings = [
       { topping: 1, bits: 2, name: 'onions' },
       { topping: 2, bits: 4, name: 'mushrooms' },
       { topping: 3, bits: 8, name: 'peppers', checked:undefined /*may be*/ }
    ];
    

    使用 AngularJs $WatchCollection 观看:

    我们将创建一个我们正在观察集合的元素的属性数组(.checked),而不是监视上面的 objects 数组,它可以更改对象中的任何属性。

    我们filter 数组的元素只监视那些定义了.checkedmap 的元素到角度数组watchCollection

    当发生更改时,我将比较 (.checked) 的新旧数组,以使用 lodash difference 方法获得准确更改的元素。

     $scope.$watchCollection(
    
                    // Watch Function
                    () => (
                       $scope
                         .toppings
                         .filter(tp => tp.checked !== undefined)
                         .map(tp => tp.checked)
                     ),
    
                    // Listener
                    (nv, ov) => {
                        // nothing changed
                        if(nv == ov || nv == "undefined") return;
    
                        // Use lodash library to get the changed obj
                        let changedTop = _.difference(nv,ov)[0];
                    
                        // Here you go..
                        console.log("changed Topping", changedTop);
                })
    

    【讨论】:

    • 在我看来这是更好的答案之一,它对数组的效果要好得多,但是为了问题作者和其他寻求帮助的人,你应该添加更多细节回答您的答案以解释您正在使用类型脚本,并解释您过滤和映射数组的理由。否则是一个很好的解决方案。
    • 这不是 TypeScript 而是 Javascript ES6 :-)。因应用过滤器而编辑。
    • 我认为这只适用于非常特殊的情况(恰好是 OP,正如他们在评论中提到的那样),被监视的属性不超过两个状态。
    【解决方案2】:

    您使用 MAP 收集您需要的所有属性值 + 将它们转换为一个小的字符串表示形式(在本例中为 1 和 0),然后将它们连接在一起形成一个可以观察到的字符串。

    打字稿示例:

            $scope.$watch(
                () => this.someArray.map(x => x.selected ? "1" : "0").join(""),
                (newValue, oldValue, scope) => this.onSelectionChanged(this.getSelectedItems()));
    

    【讨论】:

    • 我怀疑这比首先执行 filter 然后执行 map 稍微好一点,就像在 @ahmadalibaloch 的另一个答案中一样
    【解决方案3】:

    $scope.$watch 的 watchExpression 参数应该是字符串或函数。我没有对此进行广泛的实验(我尽量避免显式监视),但我认为当您将“简单”范围属性视为对象引用时它也可以工作,但对于更复杂的引用就不那么好了。

    我认为如果您将引用作为字符串提供,例如'model[' + key + '].checked' 那么你可能会取得一些成功(我之所以这么说是因为我之前用 $watchCollection 做过类似的事情)。

    或者你应该能够提供一个函数,例如

    $scope.$watch(function() { return scope.model[key].checked; }, function() { ... });

    希望这会有所帮助!

    【讨论】:

    • 这种方法的缺点是您要为对象上的每个键创建一个观察者。 $watchCollection 更具性能。
    • @William Lepinski:你能解释一下为什么数组上的 watchCollection 必须监视每个项目的每个属性,它的性能会比只监视我需要监视的属性更好吗?我不是不同意,我只是想知道这是怎么做到的。深度手表是如何实现的?
    • @Tim watchCollection 进行浅层监视,即它不监视数组的每个属性它只是监视从/向数组推送/弹出/覆盖的内容(ng-repeat 使用 watchCollection)..
    【解决方案4】:

    请改用$watchCollection

    来自文档:

    $watchCollection(obj, listener); Shallow 监视对象的属性并在任何属性更改时触发(对于数组,这意味着监视数组项;对于对象映射,这意味着监视属性)。如果检测到更改,则触发侦听器回调。

    通过标准的 $watch 操作观察 obj 集合,并在每次调用 $digest() 时检查是否添加、删除或移动了任何项目。 每当 obj 中的任何内容发生更改时,都会调用侦听器。示例包括添加、删除和移动属于对象或数组的项目。

    【讨论】:

    • 但它监视集合的所有属性。不只是一个单一的财产。在这种情况下,OP 只想观看 checked 属性。
    • 好吧,您可以只抓取选中的属性。将这个新创建的数组分配给作用域并使用 $watchCollection 来监视检查的属性数组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 2018-04-10
    • 2017-11-18
    • 2019-09-21
    相关资源
    最近更新 更多