【问题标题】:Watching for model change关注模型变化
【发布时间】:2015-12-30 04:02:51
【问题描述】:

我的指令的链接函数中有以下代码:

link: function (scope, elem, attrs, ngModel) {
        $(elem).datagrid({
            columns: [[
                { field: 'ck', checkbox: 'true' },
                { field: 'ProjectID', title: 'Project ID', width: '30%' },
                { field: 'Name', title: 'Name' }
            ]]                
        });
        ngModel.$render = function (value) {
            $(elem).datagrid('loadData', ngModel.$viewValue);
        };
        scope.$watch('projectList', function (newValue, oldValue) {
            $(elem).datagrid('loadData', ngModel.$viewValue);
        });

    }

当最初为数组 $scope.projectList 分配数据时,两个侦听器都会被触发。在我的控制器的某个地方(仅用于测试)我正在向 $scope.projectList 添加另一个元素:

$scope.test = function () {
    var project = $scope.projectList[0];
    $scope.projectList.push(project);
}

此时没有任何听众被解雇。有人可以解释为什么会这样吗?

谢谢

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    $watch 仅检查对projectList 数组的引用 是否已更改,它不会对集合执行深度监视。当您将数组分配给范围变量时,您会更改此引用,但随后修改此数组会使引用保持不变。在您的情况下,使用$watchCollection() 方法似乎更合适。

    值得注意的是,$watchCollection 仅检查集合元素引用是否已更改,例如通过添加/删除/替换项目。它不检查这些元素本身是否已被修改。

    如果您想深入了解您的收藏,请将true 作为第三个参数传递给$watch()

    scope.$watch('projectList', function (newValue, oldValue) {
        $(elem).datagrid('loadData', ngModel.$viewValue);
    }, true);  // <--- note the objectEquality flag set to true
    

    但是请注意,如果集合中的项目很复杂并且需要更多时间来比较它们,这可能会影响性能。

    您还可以查看Angular docs 以获取更多信息(向下滚动查看$watch()$watchCollection() 方法说明)。

    【讨论】:

    • 似乎更改为 $watchCollection 做到了。非常感谢您的帮助!
    • 我很高兴听到这个消息。我还更新了答案,对幕后发生的事情以及$watchCollection() 何时可能还不够(取决于需要检测哪些更改)进行了一些额外的解释。
    • 还有一件事。为什么在我修改集合时 ngModel.$render 也没有触发?
    【解决方案2】:

    这是因为普通的 $watch 函数只查看引用相等性,所以如果你这样做:

    var project = $scope.projectList[0];
    $scope.newProjectList = [];
    $scope.newProjectList.push(project);
    $scope.projectList = $scope.newProjectList;
    

    然后它会触发你的手表,因为 $scope.projectList 的对象引用发生了变化。

    如果你想要你的例子:

    var project = $scope.projectList[0];
    $scope.projectList.push(project);
    

    要触发手表,那么您必须这样做

    scope.$watch('projectList', function (newValue, oldValue) {
        $(elem).datagrid('loadData', ngModel.$viewValue);
    }, true);
    

    (将 true 作为最后一个参数传递给 $watch 会导致 $watch 进行深度相等比较,这对于大对象或大列表可能会很慢)

    scope.$watchCollection('projectList', function (newValue, oldValue) {
        $(elem).datagrid('loadData', ngModel.$viewValue);
    });
    

    (这在引用相等性方面类似于常规的 $watch,但它是专门为列表制作的。因此,除了主要的引用检查之外,它还对集合中的每个项目进行引用检查或数组,所以它会从 .push 和 .pop 之类的东西触发)

    它们都有自己的优势,具体取决于您要查找的支票类型。另外,请记住 $watch 返回一个注销函数,您可以使用它来清除它,您通常会在 scope.$on('$destroy' 内执行此操作。如果不这样做,它们只会停留一段时间,然后如果你有很多,可能会浪费。

    这里不错 article 3 款手表的区别

    【讨论】:

      猜你喜欢
      • 2019-10-13
      • 1970-01-01
      • 1970-01-01
      • 2015-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多