$ watch'ing Angular指令中的数据更改


132

$watch在处理内部数据(例如,插入或删除数据)时如何在Angular指令中触发变量,但不给该变量分配新对象?

我有一个当前从JSON文件加载的简单数据集。我的Angular控制器可以做到这一点,并定义一些功能:

App.controller('AppCtrl', function AppCtrl($scope, JsonService) {
    // load the initial data model
    if (!$scope.data) {
        JsonService.getData(function(data) {
            $scope.data = data;
            $scope.records = data.children.length;
        });
    } else {
        console.log("I have data already... " + $scope.data);
    }

    // adds a resource to the 'data' object
    $scope.add = function() {
        $scope.data.children.push({ "name": "!Insert This!" });
    };

    // removes the resource from the 'data' object
    $scope.remove = function(resource) {
        console.log("I'm going to remove this!");
        console.log(resource);
    };

    $scope.highlight = function() {

    };
});

我有一个<button>正确地称为$scope.add函数,并且新对象正确插入到$scope.data集合中。每次点击“添加”按钮,我设置的表格都会更新。

<table class="table table-striped table-condensed">
  <tbody>
    <tr ng-repeat="child in data.children | filter:search | orderBy:'name'">
      <td><input type="checkbox"></td>
      <td>{{child.name}}</td>
      <td><button class="btn btn-small" ng-click="remove(child)" ng-mouseover="highlight()"><i class="icon-remove-sign"></i> remove</button></td>
    </tr>
  </tbody>
</table>

但是,$scope.data在所有这些情况发生时,我设置的要监视的指令不会被触发。

我用HTML定义标签:

<d3-visualization val="data"></d3-visualization>

与以下指令关联(针对问题的完整性进行了修剪):

App.directive('d3Visualization', function() {
    return {
        restrict: 'E',
        scope: {
            val: '='
        },
        link: function(scope, element, attrs) {
            scope.$watch('val', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");
            });
        }
    }
});

我从一"I see a data change!"开始就收到该消息,但在点击“添加”按钮后再也没有收到。

$watch当我只是从data对象中添加/删除对象,而没有获取整个数据集来分配给对象时,如何触发事件data


5
如果newValue等于0,false,“”或其他类似“我看到数据已更改!”的提示,请快速提示。不会开火。例如,原始值是true,newValue是false。仅在其中使用newValue就足够了,只有在发生某些更改时才调用手表。如果它是一个对象,它将立即被调用。
Mathew Berg

好提示,谢谢!我将切换该if语句。
Evil Closet Monkey

Answers:


218

您需要启用深对象脏检查。默认情况下,angular仅检查您监视的顶级变量的引用。

App.directive('d3Visualization', function() {
    return {
        restrict: 'E',
        scope: {
            val: '='
        },
        link: function(scope, element, attrs) {
            scope.$watch('val', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");
            }, true);
        }
    }
});

范围。$ watch函数的第三个参数如果设置为true,则启用深度脏检查。

请注意,进行深层脏污检查非常昂贵。因此,如果您只需要监视children数组而不是整个data变量,则可以直接监视该变量。

scope.$watch('val.children', function(newValue, oldValue) {}, true);

版本1.2.x引入了$ watchCollection

Shallow监视对象的属性,并在任何属性更改时触发(对于数组,这意味着监视数组项;对于对象映射,这意味着监视属性)

scope.$watchCollection('val.children', function(newValue, oldValue) {});

1
工作很棒。谢谢!
Evil Closet Monkey

1
乐意效劳。继续尝试$ watch函数,因为它可以监视的表达式是多种多样的。
Liviu T. 2012年

1
感谢您指出如何仅检查深层阵列;)
veritas

很遗憾,我不能超过一次投票。.我会赏金给我,因为我失去了寻找的2小时。
Alex Arvanitidis 2015年

最好的答案在那里。
chovy

2

因为如果您想深层触发数据,则必须传递true侦听器的第3个参数。默认情况下,它是第三个参数,false它表示函数将触发,仅当变量更改时才触发它,它是field


1

指令的我的版本,该指令使用jqplot在数据可用时绘制数据:

    app.directive('lineChart', function() {
        $.jqplot.config.enablePlugins = true;

        return function(scope, element, attrs) {
            scope.$watch(attrs.lineChart, function(newValue, oldValue) {
                if (newValue) {
                    // alert(scope.$eval(attrs.lineChart));
                    var plot = $.jqplot(element[0].id, scope.$eval(attrs.lineChart), scope.$eval(attrs.options));
                }
            });
        }
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.