【问题标题】:Issue creating isolated scope in Angular + UI Bootstrap在 Angular + UI Bootstrap 中创建隔离范围的问题
【发布时间】:2014-02-21 08:43:20
【问题描述】:

我有一个使用 AngularJS 创建的简单数据表。表格的其中一列是根据控制器上的函数计算得出的。

我在页面上有一个打开新模式的按钮。当我使用 UI 引导程序打开模式时,我得到了一个新的隔离范围(根范围的子范围),正如预期的那样。但是,如果我在模式中有输入文本,则此文本字段中的任何按键都会自动调用父作用域上的函数——即使我可以验证作用域是隔离的。

这里是行为的一个 plunkr:http://plnkr.co/edit/JzhxSDcSefDe04Psxq0w

如示例所示,表格的第三列是使用名为“ageNextYear”的函数计算的。在渲染表格时,该函数会按预期多次调用(并且可以在控制台日志中进行验证)。但是,如果我打开模式并在字段中输入一些文本,父范围上的“ageNextYear”函数仍然会被调用(在输入字段中输入一些文本并观察控制台日志输出)。

我不确定这是否是预期的行为,或者我是否做错了什么。我尝试在两个作用域上使用点符号,并明确地将新作用域传递给 $modal.open,但没有任何乐趣。

我可以解决这个问题(通过在“人”上创建一个 watchCollection 并以这种方式更新表格 - 这可能是整体上这样做的更好方法)但我想验证其他人是否也看到了这种行为。

【问题讨论】:

  • 您的问题是否可能与指令 modalWindow 使用“transclude”这一事实有关?

标签: javascript angularjs twitter-bootstrap


【解决方案1】:

您遇到的问题与模态对话框的范围无关。该问题与在 ng-repeat 表达式中使用函数有关。一般来说,在表达式中使用函数是一个性能问题,但在 ng-repeat 中它是一个更大的问题。根据这篇关于common pitfalls of using scopes的优秀文章,

在视图或观察者中使用表达式时,您应该始终记住,每次 AngularJS 认为需要时都会调用表达式。使用函数不会获得最佳性能,甚至可能会错过一些更改事件。

这意味着一个表达式......

  1. 在 ng-repeat 中将分别为每个项目调用。此外,repeat 指令使用它来确定数据更改。
  2. 可以在一个摘要中多次评估。当您使用多个指令或其他范围观察器时,可能会发生这种情况。
  3. 即使直接作用域似乎没有变化,也可以进行评估。
  4. 如果函数的返回值发生变化,则不会评估包含函数,但只有在函数定义发生变化时才会评估。

您的示例导致这 4 个中的 3 个发生。

  1. 您对范围内的每个对象重复函数调用,3 项 = 3 次函数调用。
  2. 您可以通过调用模态对话框间接添加一个额外的观察者。
  3. 模态对话框范围内数据的更改会导致评估包含 ng-repeat 的控制器的范围,即使 ng-repeat 中的数据没有更改(它无法知道数据是否更改直到 $digest 被调用)。对 Modal 的每次更改都会导致 $digest,这会导致再次遍历 ng-repeat,并再次调用 ng-repeat 中的每个项目。

在您的情况下,不需要在每次评估表达式时都运行逻辑。当逻辑结果发生变化时,最好将逻辑计算并写入范围。这将逻辑与对象和视图分离。

总之,

最佳实践:

  • 请勿在表达式中使用函数。
  • 请勿在表达式中使用除范围之外的其他数据。
  • 应用外部数据更改时务必使用 $scope.$apply()。

【讨论】:

  • 谢谢,安德鲁 - 这是一个有用的文件。我很好奇-如果您要重写 plunker 示例以避免您提到的 3 项,它会是什么样子?您会使用 WatchCollection 还是其他策略?
  • 我不建议在这种情况下使用 WatchCollection,因为它不会监视集合中的深层变化。而是使用 $watch 替代语法 $watch(collection, [Equality = true])。在我的备用plnkr.co/edit/FQZwbx3jkQ3B5xp7izvG?p=preview 中,您可以看到更改何时触发。有关 watch 与 watchCollection 的更多信息,请参阅 bennadel.com/blog/…
  • 另请注意,如果我正在为此进行生产,我会进行额外检查以比较手表中的 oldValnewVal,以进一步优化更新调用
  • 谢谢,安德鲁 - 这真的很有用。一个相关的问题 - 你有没有看到任何关于在 ng-repeats 上使用函数作为搜索过滤器的建议?我有一个按函数过滤的 ng-repeat,当摘要运行时,这也被称为“百万”次。它非常高效,因此没有任何性能命中,但我仍然很好奇。
  • 我真的需要看看过滤器在 ng-repeat 中的位置...一般来说,如果搜索词发生变化,过滤器应该只在摘要循环中触发,并且在那个例如,对于您正在过滤的数组中的每个对象,它应该只处理一次。如果它更频繁地触发,或者当搜索词没有改变时,过滤器可能没有被正确抽象。我想再提一个问题,我们可以看看。
【解决方案2】:

西蒙, 我喜欢你的问题,我在范围上添加了 watch 并看到摘要周期被调用

$scope.$watch(function watchMe(scope) { console.log('Digest 看着我!'); });

以下是带有摘要的分叉。 http://plnkr.co/edit/5PTO1uPFvmLrg7K9vzTm?p=preview

我不知道这是原因,但我认为 ng-repeat 中的表达式正在调用摘要,因为它试图评估该项目上任何事件的表达式。

我认为我们应该评估模型中的表达式并将更新的模型提供给 ng-repeat 以解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 2013-09-26
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多