在AngularJS中编写指令时,如何确定是否不需要新的作用域,新的子作用域或新的隔离作用域?

时间:2013-02-16 19:26:05

标签: angularjs angularjs-directive angularjs-scope

我正在寻找一些可以用来帮助确定在编写新指令时使用哪种类型的范围的指南。理想情况下,我想要一个类似于流程图的东西,它会引导我解决一堆问题并弹出正确答案 - 没有新的新范围,新的子范围或新的隔离范围 - 但这可能要求太多。这是我目前微不足道的指导方针:

我知道在元素上使用带有隔离范围的指令会强制同一元素上的所有其他指令使用相同(一个)隔离范围,因此当使用隔离范围时,这不会严重限制?

我希望Angular-UI团队(或其他已经写过很多指令的人)可以分享他们的经验。

请不要添加简单说“为可重用组件使用隔离范围”的答案。

5 个答案:

答案 0 :(得分:289)

这是一个很棒的问题!我听取其他人的意见,但以下是我使用的指南。

高空前提:范围被用作"胶水"我们用来在父控制器,指令和指令模板之间进行通信。

父级范围: scope: false,因此根本没有新的范围

我不经常使用它,但正如@MarkRajcok所说,如果指令不能访问任何范围变量(显然没有设置任何!)那么这就好了就我而言。这对于在父指令的上下文中仅使用 的子指令也很有用(虽然总有例外),并且没有模板。基本上任何带有模板的东西都不属于共享范围,因为你本身就暴露了访问和操作的范围(但我确定这个规则有例外)。

作为一个例子,我最近创建了一个指令,用于在编写过程中使用SVG库绘制(静态)矢量图形。它$observe有两个属性(widthheight)并在其计算中使用它们,但它既不设置也不读取任何范围变量,也没有模板。这是一个不创建另一个范围的好用例;我们不需要一个,所以为什么要这么麻烦?

但是在另一个SVG指令中,我需要使用一组数据,另外还需要存储一小部分状态。在这种情况下,使用父范围将是不负责任的(再次,一般来说)。所以相反......

儿童范围: scope: true

具有子范围的指令具有上下文感知功能,旨在与当前范围进行交互。

显然,这相对于隔离范围的一个关键优势是用户可以自由地对他们想要的任何属性使用插值;例如在具有隔离范围的指令上使用class="item-type-{{item.type}}"在默认情况下不起作用,但在具有子范围的指令上工作正常,因为在父范围内仍可以找到内插的内容。此外,该指令本身可以安全地评估其自身范围内的属性和表达,而不必担心父母的污染或损害。

例如,工具提示只是添加的东西;隔离范围不起作用(默认情况下见下文),因为我们希望在这里使用其他指令或插值属性。工具提示只是一个增强功能。但是工具提示还需要在范围上设置一些用于子指令和/或模板的东西,显然要管理自己的状态,因此使用父范围确实非常糟糕。我们要么污染它要么破坏它,也不是bueno。

我发现自己使用子范围的频率高于隔离范围或父范围。

隔离范围: scope: {}

这适用于可重复使用的组件。 :-)

但严肃地说,我认为"可重复使用的组件" as"自包含组件"。目的是将它们用于特定目的,因此将它们与其他指令组合或向DOM节点添加其他内插属性本身并不合理。

更具体地说,此独立功能所需的任何内容都是通过在父作用域的上下文中评估的指定属性提供的;它们是单向字符串(' @'),单向表达式('&'),或双向变量绑定(' =&# 39)。

对于自包含组件,需要在其上应用其他指令或属性是没有意义的,因为它本身就存在。它的样式由自己的模板(如果需要)控制,并且可以将适当的内容转换(如果需要)。它是独立的,所以我们把它放在一个孤立的范围内也说:"不要搞砸这个。我通过这几个属性为您提供了一个定义的API。"

一个好的最佳实践是尽可能多地从指令链接和控制器函数中排除基于模板的内容。这提供了另一种类似API的"配置点:指令的用户可以简单地替换模板!功能都保持不变,其内部API从未被触及,但我们可以根据需要混淆样式和DOM实现。 ui / bootstrap是一个伟大的示例,说明如何做到这一点,因为Peter&帕维尔非常棒。

隔离范围也适用于翻译。抓住标签;它们不仅仅是整个功能,而且 in 内的任何内容都可以在父范围内自由评估,同时让标签(和窗格)可以做任何他们想做的事情。选项卡显然有自己的状态,它属于范围(与模板交互),但该状态与使用它的上下文无关 - 它完全是内部使tab指令成为tab指令的内部。此外,使用选项卡的任何其他指令没有多大意义。他们是标签 - 我们已经有了这个功能!

使用更多功能对其进行环绕或转换更多功能,但该指令已经是它的基础。

所有这一切,我应该注意到,有一些方法可以解决隔离范围的某些限制(即功能),正如@ProLoser在他的回答中所暗示的那样。例如,在子范围部分中,我提到了在使用隔离范围时默认情况下非指令属性的插值(默认情况下)。但是,用户可以简单地使用class="item-type-{{$parent.item.type}}",它将再次起作用。因此,如果有令人信服的理由在子范围内使用隔离范围,但您担心其中一些限制,请知道如果需要,您可以解决几乎所有问题。

<强>摘要

没有新范围的指令是只读的;他们完全信任(即应用内部),他们不会触摸插孔。具有子范围的指令添加功能,但它们不是唯一的功能。最后,隔离范围适用于作为整个目标的指令;它们是独立的,所以它可以(并且大多数&#34;正确&#34;)让它们变得流氓。

我想了解最初的想法,但是当我想到更多的事情时,我会更新这个。但神圣的废话 - 这是一个很长的回答......


PS:完全相切,但由于我们正在谈论范围,我更愿意说&#34;原型&#34;而其他人更喜欢&#34; prototypal&#34;,这似乎更准确但只是从舌头上滚下来并不是很好。 : - )

答案 1 :(得分:52)

我的个人政策和经验:

隔离:私人沙箱

我想创建许多范围方法和变量,这些方法和变量仅由我的指令使用,并且从未被用户看到或直接访问过。我想将可用的范围数据列入白名单。 我可以使用transclusion来允许用户跳回到父范围(不受影响)。我 NOT 希望我的变量和方法可以在被饶恕的孩子中使用。

儿童:内容的一个小节

我想创建范围方法和变量,用户可以访问 CAN ,但与我的指令上下文之外的周围范围(兄弟和父母)无关。我还想让所有父范围数据透明地渗透。

无:简单,只读指令

我真的不需要搞乱范围方法或变量。我可能正在做一些与范围无关的事情(例如显示简单的jQuery插件,验证等)。

注释

  • 您不应该让ngModel或其他内容直接影响您的决定。您可以通过执行ng-model=$parent.myVal(子)或ngModel: '='(隔离)等操作来规避奇怪的行为。
  • 隔离 + 转换会将所有正常行为恢复为兄弟指令并返回到父作用域,所以不要让这影响您的判断。
  • 不要弄乱 none 上的范围,因为它就像将数据放在DOM的下半部分的范围内,而不是上半部分,这是有道理的。
  • 注意指令优先级(没有具体的例子说明这会影响事情)
  • 注入服务或使用控制器跨任何范围类型的指令进行通信。您也可以require: '^ngModel'查看父元素。

答案 2 :(得分:18)

在编写了很多指令后,我决定使用较少的isolated范围。即使它很酷并且您封装了数据并且确保不会将数据泄漏到父作用域,但它严重限制了您可以一起使用的指令数量。所以,

如果您要编写的指令本身将完全完全,并且您不打算与其他指令共享,请转到隔离范围 。 (就像你可以插入它的一个组件,对最终开发人员没有太多的自定义)(当你尝试编写其中包含指令的子元素时,它变得非常棘手)

如果您要编写的指令是只是进行dom操作,这些操作不需要内部状态范围或显式范围更改(大多数都是非常简单的事情);去没有新的范围。 (例如ngShowngMouseHoverngClickngRepeat

如果您要编写的指令需要更改父作用域中的某些元素,还需要处理某些内部状态,请转到新的子作用域。 (例如ngController

请务必查看指令的源代码:https://github.com/angular/angular.js/tree/master/src/ng/directive
它对如何思考它们有很大帮助

答案 3 :(得分:9)

我只是想加上我目前的理解以及它与其他JS概念的关系。

默认(例如,未声明或范围:false)

这在哲学上等同于使用全局变量。您的指令可以访问父控制器中的所有内容,但它也会影响它们并同时受到影响。

<强>范围:{}

这就像一个模块,任何想要使用的东西都需要明确传递。如果您使用的每个指令都是隔离范围,那么它可以等同于使每个JS文件编写自己的模块,并且在注入所有依赖项时会产生大量开销。

范围:孩子

这是全局变量和显性直通之间的中间地带。它类似于javascript的原型链,只是向您扩展了父范围的副本。如果您创建隔离范围并传入父范围的每个属性和功能,则它在功能上等同于此。


关键是任何指令都可以用任何方式编写。不同的范围声明可以帮助您组织。你可以把一切都变成一个模块,或者你可以只使用所有的全局变量并且非常小心。为了便于维护,尽管将逻辑模块化为逻辑上连贯的部分更为可取。开放式草地和封闭式监狱之间存在平衡。 我觉得这很棘手的原因是,当人们了解这一点时,他们认为他们正在了解指令是如何工作的,但实际上他们正在学习代码/逻辑组织。

帮助我弄清楚指令如何工作的另一件事是学习ngInclude。 ngInclude帮助您包含html部分。当我第一次开始使用指令时,我发现你可以使用它的模板选项来减少代码,但我并没有真正附加任何逻辑。

当然,在有角度的指令和angular-ui团队的工作之间,我还没有必须创建我自己的指令来做任何实质性的事情,所以我对此的看法可能是完全错误的。

答案 4 :(得分:2)

我同意Umur。从理论上讲,隔离的范围听起来很棒并且“可移植”,但是在构建我的应用程序以涉及非平凡的功能时,我遇到了需要合并几个指令(一些嵌套在其他指令中或为它们添加属性)以便完全写入我的拥有HTML,这是域特定语言的目的。

最后,在指令的每个DOM调用上使用多个属性传递链中的每个全局或共享值是非常奇怪的(如隔离范围所需)。它只是看起来很愚蠢,反复写入DOM中的所有内容,即使这些是共享对象,它也感觉效率低下。它也不必要地使指令声明复杂化。使用$ parent来“伸出”并从指令HTML中获取值似乎非常糟糕。

我也改变了我的应用程序,主要是包含极少数隔离区的子范围指令 - 只有那些不需要从父级访问任何东西的东西,除了它们可以通过简单的,非重复的传递属性。

在有这样的事情之前几十年梦想领域特定语言的梦想,我很高兴AngularJS提供了这个选项,我知道,随着越来越多的开发人员在这个领域工作,我们会看到一些非常很酷的应用程序,他们的架构师也可以轻松编写,扩展和调试。

- D