我正在寻找一些指导方针,可以用来帮助确定在编写新指令时使用哪种类型的范围。理想情况下,我想要类似于流程图的东西,它引导我完成一堆问题并弹出正确答案——没有新的新范围、新的子范围或新的隔离范围——但这可能要求太多. 这是我目前的一套微不足道的指导方针:
如果将使用指令的元素使用 ng-model,请不要使用隔离范围
如果指令不修改任何范围/模型属性,请不要创建新范围
我知道在元素上使用具有隔离作用域的指令会强制同一元素上的所有其他指令使用相同的(一个)隔离作用域,所以这不会严重限制何时可以使用隔离作用域吗?
我希望 Angular-UI 团队中的一些人(或其他编写过许多指令的人)可以分享他们的经验。
请不要添加简单地说“为可重用组件使用隔离范围”的答案。
多么棒的问题啊!我很想 听听 其他人的意见,但这里是我使用的指导方针。
高空前提:作用域作为我们用来在父控制器、指令和指令模板之间进行通信的“胶水”。
Parent Scope: scope: false,所以根本没有新的范围
scope: false
我不经常使用它,但正如@MarkRajcok 所说,如果指令不访问任何范围变量(并且显然没有设置任何变量!)那么就我而言这很好。 这对于仅 在父指令上下文中使用且没有模板的子指令也很有帮助(尽管总是有例外)。基本上,任何带有模板的东西都不属于共享范围,因为您天生就暴露了该范围以进行访问和操作(但我确信这条规则有例外)。
例如,我最近创建了一个指令,该指令使用我正在编写的 SVG 库绘制(静态)矢量图形。它$observe是两个属性(width和height)并在计算中使用它们,但它既不设置也不读取任何范围变量,也没有模板。这是不创建另一个范围的好用例;我们不需要,那何必呢?
$observe
width
height
但是,在另一个 SVG 指令中,我需要使用一组数据,并且还必须存储一点点状态。在这种情况下,使用父作用域是不负责任的(同样,一般来说)。所以与其…
子范围: scope: true
scope: true
具有子作用域的指令是上下文感知的,旨在与当前作用域交互。
显然,与隔离范围相比,它的一个关键优势是用户可以自由地对他们想要的任何属性使用插值;例如,class="item- type-{{item.type}}"在具有隔离范围的指令上使用默认情况下将不起作用,但在具有子范围的指令上可以正常工作,因为默认情况下仍然可以在父范围中找到内插的任何内容。此外,指令本身可以在其自身范围的上下文中安全地评估属性和表达式,而不必担心对父级的污染或损坏。
class="item- type-{{item.type}}"
例如,工具提示是刚刚添加的东西;隔离范围不起作用(默认情况下,见下文),因为预计我们将在此处使用其他指令或插值属性。工具提示只是一个增强。但是工具提示还需要在范围上设置一些东西以与子指令和/或模板一起使用,并且显然是为了管理自己的状态,因此使用父范围确实很糟糕。我们要么污染它,要么破坏它,布埃诺也不是。
我发现自己使用子范围比隔离或父范围更频繁。
隔离范围: scope: {}
scope: {}
这适用于可重用组件。:-)
但说真的,我认为“可重用组件”是“独立组件”。意图是它们将用于特定目的,因此将它们与其他指令组合或向 DOM 节点添加其他插值属性本质上是没有意义的。
更具体地说,这个独立功能所需的任何东西都是通过在父范围的上下文中评估的指定属性提供的;它们可以是单向字符串 (‘@’)、单向表达式 (‘&’) 或双向变量绑定 (‘=’)。
在自包含组件上,需要对其应用其他指令或属性是没有意义的,因为它本身就存在。它的样式由它自己的模板管理(如果需要),并且可以嵌入适当的内容(如果需要)。它是独立的,所以我们把它放在一个隔离作用域中也是为了说:“不要搞砸了。我通过这几个属性给你一个定义好的 API。”
一个好的最佳实践是从指令链接和控制器函数中排除尽可能多的基于模板的东西。这提供了另一个“类似 API”的配置点:指令的用户可以简单地替换模板!功能都保持不变,并且它的内部 API 从未被触及,但我们可以尽可能多地处理样式和 DOM 实现。ui/bootstrap 是一个 很好的 例子,因为 Peter & Pawel 很棒。
隔离范围也非常适合与嵌入一起使用。取标签;它们不仅是整个功能,而且其中的任何内容都可以在父范围内自由评估,同时让选项卡(和窗格)做任何他们想做的事情 。 选项卡显然有自己的 状态 ,它属于范围(与模板交互),但该状态与使用它的上下文无关 - 它完全是使选项卡指令成为选项卡指令的内部原因。此外,将任何其他指令与选项卡一起使用没有多大意义。它们是标签——我们已经有了这个功能!
用更多功能包围它或嵌入更多功能,但指令就是它已经是什么。
综上所述,我应该注意到,正如@ProLoser 在他的回答中暗示的那样,有一些方法可以绕过隔离范围的一些限制(即功能)。例如,在子范围部分,我提到了在使用隔离范围(默认情况下)时非指令属性中断的插值。但是,例如,用户可以简单地使用class="item- type-{{$parent.item.type}}"它,它会再次工作。因此,如果有令人信服的理由在子范围上使用隔离范围,但您担心其中的一些限制,请知道如果需要,您几乎可以解决所有这些限制。
class="item- type-{{$parent.item.type}}"
概括
没有新范围的指令是只读的;他们是完全受信任的(即应用程序内部的),而且他们不会碰千斤顶。具有子范围的指令 添加 功能,但它们不是 唯一的 功能。最后,隔离范围适用于作为整个目标的指令;他们是独立的,所以让他们流氓是可以的(也是最“正确的”)。
我想表达我最初的想法,但是当我想到更多事情时,我会更新这个。但是神圣的废话 - 这是一个很长的答案......
PS:完全切题,但由于我们在谈论范围,我更喜欢说“原型”,而其他人更喜欢“原型”,这似乎更准确,但只是不太好说出来。:-)