众所周知,使用 AngularJS 时必须在指令中操作 DOM 元素。
但是,在某些用例中,在服务内部操作 DOM 似乎是可以接受的。Misko Hevery 在这里谈论这个。您还可以在Bootstrap UI Dialog中找到一个示例。
Misko 的解释相当含糊,所以我想知道您如何确定何时需要将 DOM 放入服务而不是指令中。
众所周知,使用 AngularJS 时必须在指令中操作 DOM 元素。
但是,在某些用例中,在服务内部操作 DOM 似乎是可以接受的。Misko Hevery 在这里谈论这个。您还可以在Bootstrap UI Dialog中找到一个示例。
Misko 的解释相当含糊,所以我想知道您如何确定何时需要将 DOM 放入服务而不是指令中。
一个指令,以其定义的方式,总是附加到一个 DOM 节点。因此,当您定义一个指令时,它会“扩展”或替换它所附加的 DOM 节点。
在某些情况下(如对话框),您将无法将 DOM 节点附加到任何特定的父节点。在这些情况下,使用服务是有意义的,并且控制器仍然可以远离 DOM 位,因为 DOM 操作将被封装在服务中。
弹出窗口可能是我们可能使用服务的另一种情况,但与对话框不同,弹出窗口附加到 DOM 节点。因此,即使这也只是一个灰色区域。
所以,一个基本而简单的测试是,“这段 DOM 操作代码可以附加到 DOM 节点上吗?” 如果是,则指令。如果没有,那么服务。
对话框和自定义确认框是您使用服务的典型示例。
虽然我认为 Ganaraj 已经很好地描述了 Misko 所说的内容,但您可以(并且可能应该)争辩说,用于模态对话(例如)的 DOM 操作代码可以放入 DOM 节点中。
一种方法是始终将对话框指令附加到 DOM,然后用于ng-show
有条件地显示它。然后,您可以使用$rootScope
或更好的方式与模式对话框进行通信:服务。
我实际上更喜欢这种方法,因为它感觉“正确” - 服务处理数据传输并且指令与服务交互以确保它以对用户有意义的方式显示。
但我认为,Misko 对此并不是特别清楚这一事实表明它是相当主观的。做对你最有意义的事情。
我正在谷歌搜索这个主题以加强我对此的内在感受,因为我现在正在处理一个问题,这让我想在服务中放置某些逻辑。这是我的案例,我认为将基于 dom 的处理投入使用是一个很好的理由:
我有基于指令的元素,它们对全局的鼠标位置做出反应(例如,它们根据鼠标位置以某种方式移动或改变)。这些元素的数量不定,它们不属于应用程序中的任何特定位置(因为它是一个 GUI 元素,可以属于任何地方的任何容器)。如果我要遵守严格的角度“仅指令中的 dom 逻辑”规则,那么效率会降低,因为所有元素都共享与解析鼠标位置有关的逻辑(有效地),它围绕 window.requestAnimationFrame 滴答声。
如果我将该逻辑捆绑到指令中,我将有一个监听器/raf 循环绑定到每个实例。虽然它仍然是 DRY,但效率不高,因为在每次移动鼠标时,您都会触发完全相同的侦听器,该侦听器将为每个元素返回完全相同的结果。
在这种情况下,最好将其移动到服务中,尽管它是基于 dom 的逻辑,并针对服务注册每个指令实例,以针对每个实例重复的执行逻辑调用相同的、基于实例的逻辑。
请记住,虽然 Angular 提供了一些关于如何构建代码的非常好的建议,但这并不能使它成为绝对的硬性规则,因为它不可能涵盖所有用例。如果您看到“最佳实践”似乎失败的漏洞,那是因为您实际上正确理解了最佳实践,并且您现在找到了故意违反规则的理由。
那只是我的2美分!
我同意@dudewad。归根结底,服务(工厂、提供者、价值)只是 Angular 的模块模式,其限制是作为单例实现。我认为通过传递给指令链接函数的元素而不是使用文档或其他全局方法来访问 DOM 很重要。但是,我认为从指令中获得的应用于 dom 元素的逻辑存在于同一个模块中并不重要。出于 SRP 的原因,使用服务对代码进行一些分解可能是有利的,因为您可能有一段特别复杂的逻辑,因此集中测试更有意义,或者您可能希望在多个指令中使用该逻辑正如@dudewad 指出的那样。
使用基于更改变量(即ng-show="isVisible"
)的 DOM 操作方法的一个缺点是 DOM 操作发生在下一个“javascript turn”循环(何时isVisible
更新)之后。您可能需要立即更新 DOM。
例如,一个常见的场景是在转换到新路由/状态期间显示“微调器”。如果您要$scope.isVisible = true
在$routeChangeStart
/$stateChangeStart
事件上设置,然后$scope.isVisible = false
在$routeChangeSuccess
/$stateChangeSuccess
事件上设置,您将永远看不到您的ng-show
,因为整个路由/状态更改发生在一个 javascript 回合内。最好在这些事件中使用.show()
and.hide()
以便您真正看到微调器。
将这一切带回来并使其与 OP 的问题相关——在 DOM 操作是显示“微调器”模式的情况下,我会在服务期间执行此操作,并且我会使用直接 DOM 操作方法来执行此操作,而不是依赖模型更改。