AngularJS指令(Directives)实用指南
时间:2023-03-17 17:43:24
科技观察
指令(Directives)是任何AngularJS应用程序中最重要的部分。虽然AngularJS已经提供了非常丰富的指令集,但通常需要创建特定于应用程序的指令。本教程将告诉您如何自定义指令以及如何在实际项目中使用它们。在本文的***(第二部分)中,我将指导您如何使用Angular指令创建一个简单的记事本应用程序。概述用于引入新的HTML语法的指令。指令是DOM元素上的标记,它使元素以特定方式运行。例如,静态HTML不知道如何创建和显示日期选择器控件。为了让HTML识别这种语法,我们需要使用指令。指令提供了一种创建支持日期选择的元素的方法。我们将逐步介绍这是如何完成的。如果您编写过AngularJS应用程序,那么您已经使用过指令,无论您是否意识到这一点。您一定使用过简单的指令,如ng-mode、ng-repeat、ng-show等。这些指令将特定行为分配给DOM元素。例如,ng-repeat重复一个特定的元素,而ng-show有条件地显示一个元素。如果你想让一个元素支持拖动,你还需要创建一个指令来实现它。指令背后的基本思想很简单。它通过将事件侦听器附加到元素或更改DOM,使HTML真正具有交互性。jQuery透视图想象一下如何使用jQuery创建日期选择器。首先,我们在HTML中添加一个普通的输入框,然后通过jQuery调用$(element).dataPicker()将其变成一个日期选择器。但是,想一想。当设计师过来检查HTML标记时,他或她能否立即猜出该字段实际代表什么?这只是一个简单的输入框,还是一个日期选择器?您需要查看jQuery代码来确定这些。Angular的方法是用指令扩展HTML。因此,日期选择器指令可以采用以下形式:或者像这样:这种创建UI组件的方式更加直接和清晰。您可以通过查看元素轻松地了解这是什么。创建自定义指令:Angular指令可以有以下四种表现形式:1.一个新的HTML元素()2.元素的属性()3.CSSclass()4.Comments()当然我们可以控制我们的指令在HTML中的表示。让我们看一下AngularJS中典型指令的编写。指令以与控制器相同的方式注册,但它返回一个具有指令配置属性的简单对象(指令定义对象)。下面的代码是一个简单的HelloWorld指令。varapp=angular.module('myapp',[]);app.directive('helloWorld',function(){return{restrict:'AE',replace:'true',template:'
HelloWorld!!
'};});在上面的代码中,app.directive()方法在模块中注册了一个新指令。此方法的第一个参数是此指令的名称。第二个参数是一个返回指令定义对象的函数。如果您的指令依赖于其他对象或服务,例如$rootScope、$http或$compile,此时可以注入它们。该指令用作HTML中的元素,如下所示:
//OR
或者,作为属性:
//OR
如果要符合HTML5规范,可以在元素前加上x-或data-前缀。所以下面的标记也会匹配helloWorld指令:从名称中去除x-或data-前缀。然后将-或:连接字符串转换为驼峰式,然后将其与注册的指令进行匹配。这就是为什么我们在HTML中使用helloWorld指令作为hello-world。其实这和HTML对标签和属性的大小写不敏感有关。虽然上面的命令只是实现了静态文本的显示,但是还是有一些有趣的地方值得探讨。我们在指令定义过程中使用了三个属性来配置指令。下面一一介绍它们的功能。restrict–该属性用于指定指令在HTML中的使用方式(记住指令的四种表示形式)。在上面的示例中,我们使用了“AE”。所以这个指令可以用作新的HTML元素或属性。为了允许将指令用作类,我们将限制设置为“AEC”。template-这个属性指定指令被Angular编译和链接后生成的HTML标记。此属性值不必是简单的字符串。模板可能非常复杂,通常包含其他指令、表达式({{}})等。您可能会比模板更频繁地看到templateUrl。因此,理想情况下,您应该将模板放在特定的HTML文件中,并将templateUrl属性指向它。replace–此属性指定生成的HTML内容是否将替换定义此指令的HTML元素。在我们的示例中,我们将指令用作
并将replace设置为true。因此,指令编译后,生成的模板内容将替换
。最终输出是
HelloWorld!!
。如果将replace设置为默认值false,则生成的模板将插入到定义指令的元素中。打开这个plunker并右键单击“HelloWorld!!”检查元素内容以更直观地理解这一点。Link函数和Scope指令生成的模板实际上没有多大意义,除非它是在特定范围下编译的。默认情况下,指令不会创建新的子作用域。更多,它使用父范围。也就是说,如果指令存在于控制器下,它将使用该控制器的范围。要使用作用域,我们使用一个名为link的函数。它由指令定义对象中的链接属性配置。让我们更改helloWorld指令,以便当用户在输入框中输入颜色名称时,HelloWorld文本的背景颜色会自动更改。此外,当用户点击HelloWorld文本时,背景颜色会变回白色。对应的HTML标记如下:如下:app.directive('helloWorld',function(){return{restrict:'AE',replace:true,template:'
HelloWorld',链接:function(scope,elem,attrs){elem.bind('click',function(){elem.css('background-color','white');scope.$apply(function(){scope.color="白色";});});elem.bind('mouseover',function(){elem.css('cursor','pointer');});}};});我们注意到指令定义函数中的链接。它具有三个参数:scope-指令的范围。在我们的例子中,指令的范围是父控制器的范围。elem-jQLite(jQuery的子集)指令包装DOM元素。如果你在AngularJS之前导入了jQuery,那么这个元素就是一个jQuery元素,而不是一个jQLite元素。由于这个元素已经被jQuery/jQLite包裹起来了,所以我们在做DOM操作的时候就不需要用$()来包裹了。attr–一个规范化的参数对象,包含指令所在元素的属性。例如,如果您向HTML元素添加一些属性:,那么您可以通过链接函数中的attrs.someAttribute使用它。link函数主要用于给DOM元素添加事件监听器,监听模型属性变化,更新DOM。在上面的指令片段中,我们添加了两个事件,click和mouseover。单击处理程序用于重置的背景颜色,鼠标悬停处理程序将鼠标更改为指针。模板中有一个表达式{{color}},用于在父作用域中的颜色发生变化时,改变HelloWorld文本的背景颜色。这个plunker演示了这些概念。#p#compile函数编译函数用于在执行链接函数之前进行一些DOM转换。它接受以下参数:tElement-指令在attrs上的元素-元素上给出的规范化参数列表请注意,编译函数无权访问范围并且必须返回链接函数。但是如果没有设置compile函数,可以正常配置link函数,(有compile不能用link,link函数是compile返回的)。编译函数可以这样写:app.directive('test',function(){return{compile:function(tElem,attrs){//dooptionalDOMtransformationherereturnfunction(scope,elem,attrs){//linkingfunctionhere};}};});在大多数情况下,您只需要使用链接功能即可。这是因为大部分指令只需要考虑注册事件监听器、监控模型、更新DOM等,这些都可以在链接函数中完成。但是对于像ng-repeat这样的指令,DOM元素需要被克隆并重复多次,这是在链接函数执行之前由编译函数完成的。这就提出了一个问题,为什么生成过程需要两个独立的函数,为什么不能只用一个呢?要很好地回答这个问题,我们需要了解指令是如何在Angular中编译的!指令是如何编译的当应用程序启动时,Angular开始使用$compile服务遍历DOM元素。该服务根据已注册的指令在标记文本中搜索指令。一旦识别出所有指令,Angular就会执行它们的编译方法。如前所述,compile方法返回一个链接函数,该函数被添加到稍后要执行的链接函数列表中。这称为编译阶段。如果一个指令需要被克隆很多次(比如ng-repeat),compile函数在编译阶段只执行一次,复制这些模板,但是link函数是为每个复制的实例执行的。这样分开处理,让我们在性能上有了一定的提升。这也解释了为什么在编译函数中无法访问作用域对象。编译阶段之后,链接阶段开始。在这个阶段,所有收集到的链接函数都会被一一执行。指令创建的模板将在正确的范围内进行解析和处理,然后返回带有事件响应的实际DOM节点。更改指令的范围默认情况下,指令获取其父控制器的范围。但这并不适用于所有情况。如果父控制器的范围暴露给指令,那么他们可以随意修改范围的属性。在某些情况下,您的指令希望能够添加一些仅供内部使用的属性和方法。如果我们将它添加到父作用域中,它会污染父作用域。事实上,我们还有两个选择:子作用域——这个作用域在原型上继承了子作用域和父作用域。独立作用域–独立存在且不从父作用域继承的作用域。这样的作用域可以通过指令定义对象中的作用域属性进行配置。下面的代码片段是一个示例:app.directive('helloWorld',function(){return{scope:true,//useachildscopethatinheritsfromparentrestrict:'AE',replace:'true',template:'
HelloWorld!!
'};});上面的代码告诉Angular为从父作用域继承的指令创建一个新的子作用域。另一种选择,隔离作用域:app.directive('helloWorld',function(){return{scope:{},//useanewisolatedscoperestrict:'AE',replace:'true',template:'HelloWorld!!
'};});该指令使用隔离范围。当我们想要创建可重用的指令时,隔离范围非常有用。通过使用隔离作用域,我们可以确保我们的指令是自包含的,并且可以很容易地插入到HTML应用程序中。它不能在内部访问父作用域,这保证了父作用域不被污染。在我们的helloWorld指令示例中,如果我们将范围设置为{},那么上面的代码将不起作用。它将创建一个新的独立作用域,然后对应的表达式{{color}}将指向这个新的作用域,并且它的值将是未定义的。使用隔离作用域并不意味着我们不能访问父作用域的所有属性。事实上,有一些技术可以让我们访问父作用域的属性,甚至监视它们的变化。我们将在本指令系列的第二部分中讨论这些技术以及一些更高级的概念,例如控制器函数。第二部分还将与您一起使用Angular指令创建更丰富的记事本应用程序。所以,敬请期待。原文链接:Sitepoint翻译:伯乐在线-陈新伟翻译链接:http://blog.jobbole.com/62249/