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

Mar*_*cok 265 angularjs angularjs-directive angularjs-scope

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

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

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

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

Jos*_*ler 289

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

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

父范围: scope: false所以没有新的范围

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

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

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

儿童范围: scope: true

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

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

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

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

隔离范围: scope: {}

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

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

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

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

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

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

围绕它具有更多功能或转换更多功能,但该指令已经是它.

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

摘要

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

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


PS:完全切向,但由于我们谈论的是范围,我更喜欢说"原型",而其他人则更喜欢"原型",这似乎更准确,但只是完全脱离舌头.:-)

  • @ user2483724一个非常常见的误解是"可重用"指令是使用隔离范围的指令; 不是这样.如果你看一下预先打包的指令,几乎没有一个使用隔离范围 - 有些甚至不是儿童范围 - 但我保证你可以重复使用!规则应该是如何使用指令范围.如果它只是为了节省文件中的空间,我不确定指令是最好的方法.它为开发人员增加了处理时间.但如果你必须,那就去吧.或者使用`ngInclude`.或者将其作为构建的一部分.很多选择! (2认同)

Pro*_*ser 52

我的个人政策和经验:

隔离:私人沙箱

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

儿童:内容的一个小节

我要创建范围的方法和变量CAN用户访问,但是是不相关的我的指令的情况下外周边范围(兄弟姐妹和父母).我还想让所有父范围数据透明地渗透.

无:简单的只读指令

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

笔记

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


Umu*_*acı 18

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

如果您要编写的指令完全依靠它自己的行为并且您不打算与其他指令共享它,那么请选择隔离范围.(就像一个组件,你可以插入它,没有太多的自定义为最终开发人员)(当你尝试编写具有指令的子元素时它变得非常棘手)

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

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

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

  • 首先,我认为你对隔离范围有点过于苛刻.我认为它们具有更广泛的适用性,而不是你给予它们的信任,并且有一些方法可以避免你(正确地)指出我们在使用它们时遇到的许多挑战.我也不同意"对最终开发人员的定制不多" - 请参阅我的答案以获取详细信息.也就是说,你的答案既不是坏事也不是错误,它确实解决了这个问题,所以我不确定为什么会这样做.所以,+ 1. (5认同)

use*_*724 9

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

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

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

范围:{}

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

范围:孩子

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


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

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

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