AngularJS 教程:揭秘自定义指令
已发表: 2022-03-11随着 JavaScript 作为一种全栈语言的快速发展,越来越多的应用程序正在使用框架,这些框架使 Web 浏览器能够处理更多的 UI 处理,例如数据绑定、管理数据视图、转换数据和许多其他服务。 最有能力、可扩展和流行的框架之一是 AngularJS,AngularJS 框架中最有用的组件之一是一种叫做指令的东西。 AngularJS 提供了许多有用的指令,更重要的是,它为创建自定义指令提供了丰富的框架。
什么是指令? 简而言之,指令是操作并向 HTML DOM 元素添加行为的 JavaScript 函数。 指令可以非常简单或极其复杂。 因此,牢牢掌握操纵它们的许多选项和功能至关重要。
在本教程中,将探索作为指令执行的四个函数并将其应用于 DOM,并提供示例。 这篇文章假设您熟悉 AngularJS 和自定义指令。 如果您是 Angular 新手,您可能会喜欢构建您的第一个 AngularJS 应用程序的教程。
AngularJS 指令生命周期的四个功能
有许多选项可以配置,这些选项如何相互关联很重要。 在 AngularJS 编译和链接 DOM 时,每个指令都会经历类似于生命周期的过程。 指令生命周期在 AngularJS 引导过程中开始和结束,在页面呈现之前。 在指令的生命周期中,如果定义了四个不同的函数可以执行。 每个都使开发人员能够在生命周期的不同点控制和自定义指令。
这四个函数是: compile 、 controller 、 pre-link和post-Link 。
compile函数允许指令在编译和链接之前操作 DOM,从而允许它添加/删除/更改指令,以及添加/删除/更改其他 DOM 元素。
控制器功能有助于指令通信。 兄弟姐妹和孩子指令可以请求他们的兄弟姐妹和父母的控制器传达信息。
pre-link函数允许在 post-link 过程开始之前进行私有$scope操作。
post-link方法是该指令的主要工作方法。
在指令中,发生了编译后的 DOM 操作,配置了事件处理程序,监视和其他东西也是如此。 在指令的声明中,四个函数是这样定义的。
.directive("directiveName",function () { return { controller: function() { // controller code here... }, compile: { // compile code here... return { pre: function() { // pre-link code here... }, post: function() { // post-link code here... } }; } } })
通常,并非所有功能都需要。 在大多数情况下,开发人员将按照以下模式简单地创建一个控制器和后链接函数。
.directive("directiveName",function () { return { controller: function() { // controller code here... }, link: function() { // post-link code here... } } })
在此配置中,链接指的是链接后功能。
无论是定义了所有函数还是部分函数,它们的执行顺序都很重要,尤其是它们相对于 AngularJS 应用程序的其余部分的执行。
AngularJS指令函数执行相对于其他指令
考虑以下 HTML 片段,其中将指令parentDir 、 childDir和grandChildDir应用于 HTML 片段。
<div parentDir> <div childDir> <div grandChildDir> </div> </div> </div>
指令中函数的执行顺序以及相对于其他指令的执行顺序如下:
- 编译阶段
- 编译函数:parentDir
- 编译函数:childDir
- 编译函数:grandChildDir
- 控制器和预链接阶段
- 控制器功能:parentDir
- 预链接功能:parentDir
- 控制器功能:childDir
- 预链接功能:childDir
- 控制器功能:grandChildDir
- 预链接功能:grandChildDir
- 链接后阶段
- 后链接功能:grandChildDir
- 后链接功能:childDir
- 后链接功能:parentDir
AngularJS 指令函数说明:Deep Dive
编译阶段首先发生。 本质上,编译阶段将事件侦听器附加到 DOM 元素。 例如,如果特定的 DOM 元素绑定到$scope属性,则允许使用$scope属性的值对其进行更新的事件侦听器将应用于 DOM 元素。 编译过程从引导 AngularJS 应用程序的根 DOM 元素开始,并使用深度优先遍历向下遍历 DOM 的分支,首先编译父节点,然后编译其子节点,一直到叶节点。
一旦编译完成,就不能再从 DOM 中添加或删除指令(尽管可以通过直接使用编译服务来解决这个问题。下一阶段是为所有指令调用控制器和预链接函数。当控制器被调用, $scope可用,可以使用。注入控制器的$element包含编译后的模板,但不包含transcluded子内容(transcluded content是指令所在的HTML开始标签和结束标签之间的内容)根据定义,MVC 模式中的控制器只是将模型传递给视图并定义处理事件的函数。因此,指令的控制器不应修改指令的 DOM,原因有两个:它违反了控制器,并且被嵌入的子内容还没有被添加到 DOM 中。那么控制器除了修改$scope之外还能做什么呢?控制器允许子指令与 wi 进行通信父指令。 控制器函数本身应该被认为是一个控制器对象,如果子指令请求它,它将被传递到子指令的 post-link 函数中。 因此,控制器通常用于通过创建具有可由其兄弟和子指令使用的属性和方法的对象来促进指令通信。 父指令无法确定子指令是否需要其控制器,因此最好将此方法中的代码限制为子指令可以安全使用的函数和属性。

在控制器功能之后,执行预链接功能。 预链接功能对很多人来说是神秘的。 如果您阅读 Internet 和书籍中的大部分文档,人们会写道,此功能仅在极少数情况下使用,人们几乎永远不需要它。 然后,这些相同的解释未能给出可以使用它的情况的示例。
预链接功能真的一点都不复杂。 首先,如果您查看 AngularJS 源代码,您会发现预链接功能的一个很好的示例:指令ng-init使用了它。 为什么? 这只是执行涉及$scope的私有代码的好方法; 不能被同级和子指令调用的代码。 与控制器函数不同,预链接函数不会传递到指令中。 因此,它可用于执行修改其指令的$scope的代码。 指令ng-init正是这样做的。 当ng-init的 pre-link 函数执行时,它只是针对指令的$scope执行传递给指令的 JavaScript。 执行结果可通过$scope的原型继承在其控制器、预链接和后链接函数执行期间对子指令进行,但无需访问这些子指令以重新执行父预链接中的代码链接功能。 此外,该指令可能需要执行与它希望保持私有的$scope无关的其他代码。
一些有经验的 AngularJS 开发人员会说这个私有代码仍然可以放在控制器中,然后不被子指令调用。 如果该指令仅由编写它的原始开发人员使用,但如果该指令将由其他开发人员分发和重用,则该论点将成立,那么在预链接函数中封装私有代码可能非常有益。 由于开发人员永远不知道随着时间的推移他们的指令将如何被重用,保护私有代码不被子指令执行是指令代码封装的好方法。 我认为将指令通信公共代码放在控制器函数中,将私有代码放在预链接函数中是一种很好的做法。 与控制器一样,预链接不应该进行 DOM 操作,也不应该执行 transclude 函数,因为子指令的内容还没有被链接。
对于每个指令,其控制器和预链接函数在其子指令的控制器和预链接函数之前执行。 一旦所有指令的控制器和链接前阶段完成,AngularJS 就会开始链接阶段,并为每个指令执行链接后功能。 链接阶段与编译、控制器和预链接执行流程相反,从叶子 DOM 节点开始,一直到根 DOM 节点。 链接后的 DOM 遍历主要遵循深度优先路径。 当每个子指令被链接时,它的链接后功能就会被执行。
post-link 函数是自定义 AngularJS 指令中最常实现的函数。 在这个函数中,几乎可以做任何合理的事情。 可以操作 DOM(仅针对其自身和子元素),可以使用$scope ,可以使用父指令的控制器对象,可以运行 transclude 函数等。但是,有一些限制。 新指令不能添加到 DOM,因为它们不会被编译。 此外,所有 DOM 操作都必须使用 DOM 函数完成。 只需在 DOM 元素上调用html函数并传入新的 HTML 将删除在编译过程中添加的所有事件处理程序。 例如,这些将无法按预期工作:
element.html(element.html());
要么
element.html(element.html() + "<div>new content</div>");
该代码不会导致 HTML 更改,但重新分配 DOM 元素的字符串版本将删除在编译过程中创建的所有事件处理程序。 通常,post-link 函数用于连接事件处理程序, $watch es 和$observe s。
一旦所有的链接后函数都执行完毕, $scope就会应用于编译和链接的 DOM 结构,AngularJS 页面就会活跃起来。
指令功能图
下表列出了每个函数的用途、执行时可用的内容以及如何正确使用每个函数的最佳实践。
执行 命令 | 指示 功能 | DOM | 嵌入 | $范围 | 可调用 由孩子 |
---|---|---|---|---|---|
1 | 编译 | DOM 尚未编译,但模板已加载到 DOM 元素内容区域。 可以添加和删除指令。 可以使用 DOM 函数和 HTML 字符串替换来操作 DOM。 | Transclude 函数可用但已弃用且不应调用。 | 无法使用。 | 子元素不能调用函数。 |
2 | 控制器 | 已编译的 DOM 元素可用,但不应修改。 已嵌入的子内容尚未添加到 DOM 元素中。 不应发生 DOM 更改,因为这是一个控制器,并且尚未链接已嵌入的子内容。 | Transclude 函数可用但不应调用。 | $scope可用并且可以使用。 函数参数使用$injector服务注入。 | 函数被传递给链接函数的子指令,并且可以被它们调用。 |
3 | 预链接 | 已编译的 DOM 元素可用但不应修改,因为尚未链接子指令 DOM 元素。 | Transclude 函数可用但不应调用。 | $scope可用并且可以修改。 | 子指令不能调用函数。 但可以调用父指令的控制器。 |
4 | 后链接 | 已编译的 DOM 元素和子指令 DOM 元素可用。 DOM 只能用 DOM 函数修改(不能替换 HTML),并且只能添加不需要编译的内容。 不允许添加/删除指令。 | Transclude 函数可用并且可以调用。 | $scope可用并且可以使用。 | 不能被子指令调用,但可以调用父指令的控制器。 |
概括
在这篇关于 AngularJS 指令的教程中,我们了解了compile 、 controller 、 pre-link和post-link四个指令函数中的每一个的目的、执行顺序和整体能力和用途。 在这四个函数中,controller 和 post-link 是最常用的,但对于需要更好地控制 DOM 或需要私有范围执行环境的更复杂的指令,可以使用编译和 pre-link 函数。