在bootbox回调中,Angular $scope不会更新

7

这是我在 SO 上的第一个问题。

当我在作用域中的回调函数中使用 splice 切取一个数组元素时,该变化不会被反映出来。

有效:

$scope.deleteA = function() {
    if (confirm("Really delete Item 3?")) {
      $scope.itemsA.splice(2, 1);
    }
}

无法工作:

$scope.deleteB = function() {
    bootbox.confirm("Really delete Item 3?", function(answer) {
      if (answer === true) {
        $scope.itemsB.splice(2, 1);
      }
    });
}

我主要关心的是理解为什么。这对我来说比拥有一个花哨的解决方法更加重要。

我创建了一个 Plunker 来展示效果


2
在bootbox回调中,Angular不知道有任何更改发生,因此无法更新视图。 - Grundy
考虑使用 ng-boot-box,这是一个 Bootbox.js 的 AngularJS 封装。 - georgeawg
2个回答

8
任何从Angular外部更改Angular作用域变量的操作,都不会通知Angular digest系统运行digest循环以更新绑定。
bootbox回调函数中,Angular并不知道发生了什么更改,因此不会更新视图。
为解决这个问题,您需要使用$apply方法$timeout服务手动启动digest循环,如下所示。
bootbox.confirm("Really delete Item 3?", function(answer) {
  if (answer === true) {
    $scope.$apply(function(){
        $scope.itemsB.splice(2, 1);
    });
  }
});

你好@Grundy。感谢你的回答。我明白这个解决方案是有效的,但我仍然不明白为什么当更改在作用域函数中完成时,Angular会“看到”它们,但当它们在同样定义在作用域函数内的函数中完成时,Angular却无法“看到”它们。Angular的世界在哪里结束,为什么? - Danny Soul
@DannySoul,因为在每个用户事件中,如clickchangekeyup等,Angular会自动运行digest循环,并在需要时更新视图,在您的情况下——Angular不会在bootbox回调后自动运行它,您应该手动运行它。 - Grundy
@Grundy,再次感谢您的回答。但我仍然不太明白。假设$scope.name是一个包含名称的字符串。它已知于Angular。如果我从函数$scope.myFunction内更改$scope.name的值,则会在视图中反映出来。如果我从定义在$scope.myFunction内部的函数(例如setTimeout)中更改该值,则不会反映出来。为什么Angular能够在一种情况下看到它,而在另一种情况下却不能呢?为什么调用函数很重要? - Danny Soul
@DannySoul,请尝试阅读更多关于HTML编译器Angular作用域生命周期的内容。 - Grundy

0

应用作用域更改的安全方法是使用$timeout服务。

bootbox.confirm("Really delete Item 3?", function(answer) {
  if (answer === true) {
    $timeout($scope.itemsB.splice(2, 1));
  }
});

所以你不需要担心 $digest 阶段,而且你的 $apply 将会是一个高调用堆栈,请看 AngularJS Wiki - Anti-Patterns 第二点。

不要使用 if (!$scope.$$phase) $scope.$apply(),这意味着你的 $scope.$apply() 不够高在调用堆栈中。


使用$timeout是一种代码异味,它是一个更深层次问题的症状。请记住,在大多数地方(控制器、服务)中,$apply已经被指令处理事件调用了。只有在实现自定义事件回调或与第三方库回调一起工作时才需要显式调用$apply - georgeawg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接