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

Don't use an isolated scope if the element that will use the directive uses ng-model See Can I use ng-model with isolated scope? and Why formatters does not work with isolated scope? If the directive doesn't modify any scope/model properties, don't create a new scope Isolate scopes seem to work well if the directive is encapsulating a set of DOM elements (the documentation says "a complex DOM structure") and the directive will be used as an element, or with no other directives on the same element.

我知道在一个元素上使用具有隔离作用域的指令会迫使同一元素上的所有其他指令使用相同的(一个)隔离作用域,所以这不是严重限制了何时可以使用隔离作用域吗?

我希望来自Angular-UI团队的一些人(或者其他写过很多指令的人)能分享他们的经验。

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


当前回答

我的个人政策和经验:

隔离:私有沙箱

我想创建大量的作用域方法和变量,仅由我的指令使用,永远不会被用户看到或直接访问。我想列出对我可用的范围数据。我可以使用transclusion允许用户跳转回父范围(不受影响)。我不希望我的变量和方法在transclexcluded child中可访问。

儿童:内容的一部分

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

None:简单的只读指令

我真的不需要干扰作用域方法或变量。我可能正在做一些与作用域无关的事情(比如显示简单的jQuery插件、验证等)。

笔记

You should not let ngModel or other things directly impact your decision. You can circumvent odd behavior by doing things like ng-model=$parent.myVal (child) or ngModel: '=' (isolate). Isolate + transclude will restore all normal behavior to sibling directives and returns to the parent scope, so don't let that affect your judgement either. Don't mess with the scope on none because it's like putting data on scope for the bottom half of the DOM but not the top half which makes 0 sense. Pay attention to directive priorities (don't have concrete examples of how this can affect things) Inject services or use controllers to communicate across directives with any scope type. You can also do require: '^ngModel' to look in parent elements.

其他回答

我同意乌穆尔的看法。从理论上讲,孤立的作用域听起来很棒,而且“可移植”,但在构建我的应用程序时,我发现需要合并几个指令(一些指令嵌套在其他指令中,或向它们添加属性),以便完全用我自己的HTML编写,这就是领域特定语言的目的。

最后,必须在指令的每次DOM调用上传递具有多个属性的每个全局值或共享值(正如隔离作用域所要求的那样),这太奇怪了。在DOM中重复编写所有这些内容看起来很愚蠢,而且感觉效率很低,即使这些是共享对象。它还不必要地使指令声明复杂化。使用$parent来“达到”并从指令HTML中获取值的解决方法看起来非常糟糕。

我也改变了我的应用程序,大部分都是子作用域指令,只有那些不需要从父作用域访问任何东西的指令,除了可以通过简单的、非重复的属性传递的指令。

在领域特定语言出现之前,我已经梦想了几十年,我很高兴AngularJS提供了这个选项,我知道,随着越来越多的开发人员在这个领域工作,我们将看到一些非常酷的应用程序,它们的架构师也很容易编写、扩展和调试。

-- D

在编写了大量指令之后,我决定使用不那么孤立的作用域。尽管它很酷,并且封装了数据并确保不将数据泄漏到父作用域,但它严重限制了可以一起使用的指令的数量。所以,

如果你要编写的指令将完全独立运行,并且你不打算与其他指令共享它,那么就使用隔离作用域。(就像一个组件,你可以把它插进去,对于最终的开发人员来说没有太多的定制)(当你试图编写包含指令的子元素时,它变得非常棘手)

如果你要写的指令只是做dom操作,不需要内部的作用域状态,或显式的作用域更改(大多数是非常简单的事情);不要尝试新的瞄准镜。(如ngShow, ngMouseHover, ngClick, ngRepeat)

如果你要写的指令需要改变父作用域中的一些元素,但也需要处理一些内部状态,那么就使用新的子作用域。(如ngController)

一定要查看指令的源代码:https://github.com/angular/angular.js/tree/master/src/ng/directive 这对如何看待它们有很大的帮助

我的个人政策和经验:

隔离:私有沙箱

我想创建大量的作用域方法和变量,仅由我的指令使用,永远不会被用户看到或直接访问。我想列出对我可用的范围数据。我可以使用transclusion允许用户跳转回父范围(不受影响)。我不希望我的变量和方法在transclexcluded child中可访问。

儿童:内容的一部分

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

None:简单的只读指令

我真的不需要干扰作用域方法或变量。我可能正在做一些与作用域无关的事情(比如显示简单的jQuery插件、验证等)。

笔记

You should not let ngModel or other things directly impact your decision. You can circumvent odd behavior by doing things like ng-model=$parent.myVal (child) or ngModel: '=' (isolate). Isolate + transclude will restore all normal behavior to sibling directives and returns to the parent scope, so don't let that affect your judgement either. Don't mess with the scope on none because it's like putting data on scope for the bottom half of the DOM but not the top half which makes 0 sense. Pay attention to directive priorities (don't have concrete examples of how this can affect things) Inject services or use controllers to communicate across directives with any scope type. You can also do require: '^ngModel' to look in parent elements.

只是想补充一下我目前的理解,以及它与其他JS概念的关系。

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

这在哲学上等同于使用全局变量。你的指令可以访问父控制器中的所有东西,但同时也会影响它们和被影响。

范围:{}

这就像一个模块,它想要使用的任何东西都需要显式地传入。如果你使用的每个指令都是一个孤立的作用域,这就相当于让你编写的每个JS文件都有自己的模块,在注入所有依赖项时需要大量的开销。

范围:孩子

这是全局变量和显式传递之间的中间地带。它类似于javascript的原型链,只是扩展了父作用域的副本。如果您创建一个隔离作用域并传入父作用域的每个属性和函数,则在功能上与此相同。


The key is that ANY directive can be written ANY way. The different scope declarations are just there to help you organize. You can make everything a module, or you can just use all global variables and be very careful. For ease of maintenance though it's preferable to modularalize your logic into logically coherent parts.There is a balance between an open meadow and a closed jail-house. 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团队的工作之间,我还没有创建我自己的指令来做任何实质性的事情,所以我的观点可能是完全错误的。

多好的问题啊!我很想听听别人怎么说,但这里是我使用的指导方针。

高度前提:作用域被用作父控制器、指令和指令模板之间通信的“胶水”。

父作用域:作用域:false,因此根本没有新的作用域

I don't use this very often, but as @MarkRajcok said, if the directive doesn't access any scope variables (and obviously doesn't set any!) then this is just fine as far as I am concerned. This is also helpful for child directives that are only used in the context of the parent directive (though there are always exceptions to this) and that don't have a template. Basically anything with a template doesn't belong sharing a scope, because you are inherently exposing that scope for access and manipulation (but I'm sure there are exceptions to this rule).

举个例子,我最近创建了一个指令,使用我正在编写的SVG库绘制(静态)矢量图形。它$观察两个属性(宽度和高度),并在计算中使用它们,但它既不设置也不读取任何作用域变量,也没有模板。这是一个不创建另一个作用域的好用例;我们不需要,那又何苦呢?

但是,在另一个SVG指令中,我需要使用一组数据,另外还必须存储一小部分状态。在这种情况下,使用父作用域是不负责任的(同样,一般来说)。所以…

子范围:范围:true

具有子作用域的指令是上下文感知的,用于与当前作用域交互。

Obviously, a key advantage of this over an isolate scope is that the user is free to use interpolation on any attributes they want; e.g. using class="item-type-{{item.type}}" on a directive with an isolate scope will not work by default, but works fine on one with a child scope because whatever is interpolated can still by default be found in the parent scope. Also, the directive itself can safely evaluate attributes and expressions in the context of its own scope without worrying about pollution in or damage to the parent.

For example, a tooltip is something that just gets added; an isolate scope wouldn't work (by default, see below) because it is expected that we will use other directives or interpolated attributes here. The tooltip is just an enhancement. But the tooltip also needs to set some things on the scope to use with a sub-directive and/or template and obviously to manage its own state, so it would be quite bad indeed to use the parent scope. We are either polluting it or damaging it, and neither is bueno.

我发现自己更多地使用子作用域而不是孤立作用域或父作用域。

隔离范围:范围:{}

这是针对可重用组件的。: -)

但是认真地说,我认为“可重用组件”是“自包含组件”。其目的是将它们用于特定目的,因此将它们与其他指令组合或向DOM节点添加其他内插属性本质上是没有意义的。

更具体地说,这个独立功能所需的任何东西都是通过在父作用域上下文中计算的指定属性来提供的;它们要么是单向字符串('@'),要么是单向表达式('&'),要么是双向变量绑定('=')。

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

一个好的最佳实践是将尽可能多的基于模板的东西从指令链接和控制器函数中排除。这提供了另一个“类似api”的配置点:指令的用户可以简单地替换模板!所有的功能都保持不变,其内部API从未被改变,但我们可以随心所欲地改变样式和DOM实现。ui/bootstrap是如何做到这一点的一个很好的例子,因为Peter和Pawel很棒。

Isolate scopes are also great for use with transclusion. Take tabs; they are not only the whole functionality, but whatever is inside of it can be evaluated freely from within the parent scope while leaving the tabs (and panes) to do whatever they want. The tabs clearly have their own state, which belongs on the scope (to interact with the template), but that state has nothing to do with the context in which it was used - it's entirely internal to what makes a tab directive a tab directive. Further, it doesn't make much sense to use any other directives with the tabs. They're tabs - and we already got that functionality!

用更多的功能包围它,或者渗透更多的功能,但指令已经是这样了。

All that said, I should note that there are ways around some of the limitations (i.e. features) of an isolate scope, as @ProLoser hinted at in his answer. For example, in the child scope section, I mentioned interpolation on non-directive attributes breaking when using an isolate scope (by default). But the user could, for example, simply use class="item-type-{{$parent.item.type}}" and it would once again work. So if there is a compelling reason to use an isolate scope over a child scope but you're worried about some of these limitations, know that you can work around virtually all of them if you need to.

总结

没有新的作用域的指令是只读的;他们是完全可信的(即在应用程序内部),他们不接触任何东西。带有子作用域的指令增加了功能,但它们不是唯一的功能。最后,隔离作用域用于作为整个目标的指令;它们是独立的,所以让它们失控是可以的(也是最“正确的”)。

我想把我最初的想法说出来,但当我想到更多的事情时,我会更新这个。但我的天-这是一个很长的SO答案…


PS:完全离题,但既然我们在谈论范围,我更喜欢说“原型”,而其他人更喜欢说“原型”,这似乎更准确,但只是从舌头上滑下来,一点也不好。: -)