首页 文章

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

提问于
浏览
263

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

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

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

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

5 回答

  • 9

    真是个好问题!我很想听别人说的话,但这里是我使用的指导方针 .

    高空前提:范围用作我们用于在父控制器,指令和指令模板之间进行通信的“ Binders ” .

    Parent Scope: scope: false ,所以没有新的范围

    我没有't use this very often, but as @MarkRajcok said, if the directive doesn' t访问任何范围变量(显然没有设置任何!)然后就我而言这是好的 . 这对于仅在父指令的上下文中使用的子指令也很有用(尽管总是有例外),并且这些指令属于共享范围,因为您本身就暴露了访问和操作的范围(但是我确定这个规则有例外) .

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

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

    Child Scope: scope: true

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

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

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

    我发现自己比孤立或父范围更频繁地使用子范围 .

    Isolate scope: scope: {}

    这适用于可重用的组件 . :-)

    但严肃地说,我认为“可重用组件”是“独立组件” . 目的是将它们用于特定目的,因此将它们与其他指令组合或将其他内插属性添加到DOM节点本身就没有意义 .

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

    在自包含组件上,由于它本身存在,因此需要在其上应用其他指令或属性是没有意义的 . 它的样式由自己的模板(如果需要)控制,并且可以将适当的内容转换(如果需要) . 它是独立的,所以我们把它放在一个隔离的范围内也说:“不要乱用它 . 我通过这几个属性给你一个定义的API . ”

    一个好的最佳实践是排除为尽可能多的来自指令链接和控制器功能的基于模板的东西 . 这提供了另一个"API-like"配置点:指令的用户可以简单地替换模板!功能都保持不变,其内部API从未被触及,但我们可以根据需要混淆样式和DOM实现 . ui / bootstrap是如何很好地做到这一点的一个很好的例子,因为Peter&Pawel很棒 .

    隔离范围也非常适合与转换一起使用 . grab 标签;它们不仅仅是整个功能,而且它内部的任何内容都可以在父范围内自由评估,同时让标签(和窗格)可以做任何他们想做的事情 . 选项卡显然有自己的状态,属于范围(与模板交互),但该状态与使用它的上下文无关 - 使用任何其他指令很有意义's entirely internal to what makes a tab directive a tab directive. Further, it doesn'标签 . 他们是标签 - 我们已经有了这个功能!

    用更多功能包围它或者转换更多功能,但该指令已经是它的原样 .

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

    Summary

    没有新范围的指令是只读的;他们're completely trusted (i.e. internal to the app) and they don' t触摸插孔 . 具有子范围的指令添加功能,但它们不是唯一的功能 . 最后,隔离范围适用于作为整个目标的指令;它们是独立的,所以让它们变得流氓是可以的(而且大多数是"correct") .

    我想初步想出来,但是当我想到更多的事情时,我会更新这个 . 但神圣的废话 - 这是一个很长的回答......


    PS:完全相切,但由于我们谈论的是范围,我更喜欢说“原型”,而其他人更喜欢“原型”,这似乎更准确,但只是从舌头上滚下来并不是很好 . :-)

  • 285

    我的个人政策和经验:

    隔离:私人沙箱

    我想创建许多范围方法和变量,这些方法和变量仅由我的指令使用,并且从未被用户看到或直接访问过 . 我想将可用的范围数据列入白名单 . I can use transclusion to allow the user to jump back in at the parent scope (unaffected) . 我希望我的变量和方法可以在被抄袭的孩子中使用 .

    孩子:内容的一个小节

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

    无:简单,只读指令

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

    注意事项

    • 你不应该让ngModel或其他东西直接影响你的决定 . 您可以通过执行 ng-model=$parent.myVal (子)或 ngModel: '=' (隔离)等操作来规避奇怪的行为 .

    • Isolate transclude 将恢复兄弟指令的所有正常行为并返回到父作用域,因此不要让它影响您的判断 .

    • 不要弄乱 none 上的范围,因为它就像将数据放在DOM的下半部分的范围而不是上半部分,这是有意义的 .

    • 注意指令优先级(没有具体的例子说明这会影响到什么)

    • 注入服务或使用控制器跨任何范围类型的指令进行通信 . 您也可以执行 require: '^ngModel' 查看父元素 .

  • 2

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

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

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

    如果您要编写的指令需要更改父范围内的一些元素,还需要处理一些内部状态,去寻找新的子范围 . (例如 ngController

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

  • 52

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

    Default (e.g. not declared or scope: false)

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

    scope:{}

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

    scope: child

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


    关键是任何指令都可以用任何方式编写 . 不同的范围声明可以帮助您组织 . 你可以把一切都变成一个模块,或者你可以只使用所有的全局变量并且非常小心 . 为了便于维护,尽管最好将您的逻辑模块化为逻辑上连贯的部分 . 在开放的草地和封闭的监狱之间存在 balancer . The reason this is tricky I believe is that when people learn about this they think they are learning about how directives work but actually they are learning about code/logic organization.

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

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

  • 18

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

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

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

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

    • D.

相关问题