在控制器中使用角度$ watch是一种反模式吗?

时间:2014-01-29 20:42:32

标签: angularjs anti-patterns

在我永无止境的追求以“正确”的角度方式做事的过程中,我一直在阅读很多关于如何让控制器观察角度服务中模型的变化。

Some sites说在控制器上使用$ watch是绝对错误的:

  

请勿在控制器中使用$ watch。几乎在所有情况下都很难测试并且完全没有必要。使用示波器上的方法来更新手表正在改变的值。

只要你自己清理完毕,

Others就好了:

  

$ watch函数本身返回一个函数,该函数在调用时解除$ watch的绑定。因此,当不再需要$ watch时,我们只需调用$ watch返回的函数。

SO questionsother reputable sites似乎正确地说,在控制器中使用$ watch是注意角度服务维护模型中的更改的好方法。

https://github.com/angular/angular.js/wiki/Best-Practices 网站,我认为我们可以给予更多权重,直接说明$ scope。$ watch应该取代对事件的需求。但是,对于处理超过100个模型和REST端点的复杂SPA,选择使用$ watch来避免$broadcast/$emit的事件可能会以批次的手表结束。另一方面,如果我们不使用$ watch,对于非平凡的应用程序,我们最终会有大量的事件意大利面。

这是一个输/输的情况吗?这是事件和手表之间的错误选择吗?我知道你可以在很多情况下使用双向绑定,但有时你只需要需要来监听变化。

修改

Ilan Frumer的评论让我重新思考我的要求,所以也许不仅仅是询问在控制器中使用$ watch是否主观好/坏,让我用这样的方式提出问题:

哪种实现可能会首先造成性能瓶颈?让控制器监听事件(必须已广播/发出),或在控制器中设置$watch - es。请记住,大型应用。

哪种实现首先会导致维护问题:$watch - es或事件?可以说有一种耦合(紧密/松散)两种方式......事件观察者需要知道要听什么,而$watch - 外部值(如MyDataService.getAccountNumber())都需要了解事物发生在他们的范围之外。

**一年后编辑**

自从我提出这个问题以来,Angular已经改变/改进了很多,但我仍然得到+ 1,所以我想我会在提到角度团队的代码时提到它,我看到了一个关于观察者的模式控制器(或存在范围被破坏的指令):

$scope.$on('$destroy', $scope.$watch('scopeVariable', functionIWantToCall)); 这需要$ watch函数返回的内容 - 可以调用以取消注册观察者的函数 - 并在控制器被销毁时将其提供给事件处理程序。这会自动清理观察者。

控制器中的手表是否具有代码味道,如果您使用它们,我相信角度团队对此模式的使用应该成为如何使用它们的强烈建议。

谢谢!

3 个答案:

答案 0 :(得分:5)

我使用两者,因为老实说,我认为它们是针对不同问题的不同工具。

我将从我构建的应用程序中给出一个示例。我有一个复杂的WebSocket服务,它从Web套接字服务器接收动态数据模型。服务本身并不关心模型的样子,当然,控制器确实如此。

启动控制器后,它会在服务数据对象上设置$watch,以便它知道特定数据对象何时到达(例如等待Service.data.foo存在。模型诞生了,它能够绑定到它并且将一个双向数据绑定到它上面,手表变得过时,它被破坏了。

另一方面,该服务也负责广播某些事件,因为有时客户端会从服务器接收文字命令。例如,服务器可能会请求客户端在整个应用程序中发送存储在“$ rootScope”中的一些元数据。在.on()步骤$rootScope期间设置module.run()以从服务器侦听这些命令,从其他服务收集所需信息,并调用WebSocket服务以将数据发送为请求。或者,如果我使用$watch()完成此操作,我需要设置一些任意变量供其观看,例如metadataRequests我每次收到请求时都需要递增。 broadcast实现同样的事情,而不必像我们的变量那样生活在永久记忆中。

基本上,当我想要看到更改的特定值时,我使用$watch()(特别是如果我需要知道前后值),如果有更高的值,我会使用事件控制器需要了解的已达到的级别条件。

关于性能,我不能告诉你哪一个会首先出现瓶颈,但我觉得这样想会让你利用每个功能最强的功能。例如,如果您使用$on()代替$watch()来查找数据中的更改,则无法访问更改前后的值,这可能会限制您尝试执行的操作。

答案 1 :(得分:3)

所有双向数据绑定都是$watch,对ng-model提供的任何范围属性都是inputform有一个控制器允许其他指令,如ng-model和{ {1}}同步ng-model值以呈现更改时的视图。这是通过他们在DOM中注册事件来检测的。

实质上,$watch的{​​{1}}将模型中的值与内部的值进行比较。它内部的值由支持指令(输入,表单等)设置。

恕我直言您应该在角度应用程序中作出反应的唯一“事件”是用户创建的(即DOM事件)。这些是通过DOM上的指令和链接到..model的ng-model来解决的 当然也有异步,为其提供$q回调调用$ digest。

至于性能,它在角度文档中表现得非常好。它在$digest上运行。所以快点吧。什么是$digest? Angular遍历所有活动范围。每个范围都有手表。它执行的。并在那些中进行比较。如果有差异,它将再次运行。 (下一个循环)它并不那么简单,因为它的优化,但你的所有“角度代码”都在这个$ digest循环中执行。许多指令可能会使用scope.$apply(...)来调用摘要。这将导致他们更改的任何值的监视通知并执行其操作。

所以你原来的问题。这是反模式吗?绝对不是,如果你知道如何使用它。虽然我只是使用ng-model。仅仅因为它已经有1.2.10+迭代的非常聪明的人在上面工作...你的应用程序的所有其他'反应性'可以通过$ q,$ timeout等来处理。

答案 2 :(得分:1)

我认为他们都有适当的位置,对我来说,很难说停止使用其中一个。

应始终使用数据绑定来使您的数据模型与视图中的更改保持同步。我想大家都同意这一点。

我认为在控制器上使用手表来触发基于数据更改的某些操作非常有用。就像观看复杂的数据模型来计算发票的运行总额一样。或者观看模型将其触发为脏。

我在发送消息或从一个范围到另一个范围的某些变化的指示时使用了broadcast / emit / on,这可能是几层之外。我创建了一个自定义指令,其中广播事件已被用作钩子以在控制器中执行某些操作。