$ watch未在阵列更改时触发

kmk*_*emp 55 angularjs angularjs-scope

我想弄清楚为什么我$watch没有被触发.这是相关控制器的片段:

$scope.$watch('tasks', function (newValue, oldValue) {
    //do some stuff
    //only enters here once
    //newValue and oldValue are equal at that point
});

$scope.tasks = tasksService.tasks();

$scope.addTask = function (taskCreationString) {
    tasksService.addTask(taskCreationString);//modifies tasks array
};
Run Code Online (Sandbox Code Playgroud)

在我看来,tasks显然正在更新正确,因为我的长度限制如下:

<span>There are {{tasks.length}} total tasks</span>
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

Mar*_*cok 135

试试$watch('tasks.length', ...)$watch('tasks', function(...) { ... }, true).

默认情况下,$ watch不检查对象是否相等,仅供参考.因此,$watch('tasks', ...)将始终只返回相同的数组引用,这不会改变.

更新: Angular v1.1.4添加了一个$ watchCollection()方法来处理这种情况:

Shamp监视对象的属性,并在任何属性发生更改时触发(对于数组,这意味着要观察数组项,对于对象映射,这意味着要观察属性).如果检测到更改,listener则会触发回调.

  • 这减轻了许多痛苦和悲伤,谢谢! (3认同)

Tom*_*Tom 13

@Mark非常好的答案.除了他的回答,还有一个$watch你应该注意的功能的重要功能.

随着$watch函数声明如下:

$watch(watch_expression, listener, objectEquality)
Run Code Online (Sandbox Code Playgroud)

$watch 监听器仅当从当前的价值函数被调用监视表达式(在你的情况下,它是'tasks')和上一次调用观看表达是不相等的.Angular保存对象的值以供以后比较.因此,观看复杂的选项将会产生不利的内存和性能影响.基本上更简单的手表表达值越好.

  • +1.我将提到(为了完整性)最初调用一次监听器函数以初始化观察者.如果`watch_expression`没有改变_,则需要发生这种情况. (5认同)

Bri*_*son 5

我建议尝试

$scope.$watch('tasks | json', ...)
Run Code Online (Sandbox Code Playgroud)

这将捕获对tasks数组的所有更改,因为它将序列化数组作为字符串进行比较.

  • 作为警示,此解决方案将涉及在每个摘要周期上序列化为JSON,对性能不是很好(特别是如果对象是复杂的或长数组).它会*起作用,但它有点像一个"敲打坚果"的大锤. (3认同)