이 문서의 예에서는 AngularJS 명령어의 사용법을 설명합니다. 다음과 같이 참조할 수 있도록 모든 사람과 공유하세요.
지시문은 모든 AngularJS 애플리케이션에서 가장 중요한 구성 요소입니다. AngularJS에는 많은 지시문이 제공되지만 일부 특별한 지시문을 직접 만들어야 하는 경우가 종종 있습니다. 이 기사에서는 사용자 지정 지시문을 안내하고 실제 Angular 프로젝트에서 이를 사용하는 방법을 설명합니다. 기사 마지막 부분에서는 Angular 지침을 사용하여 간단한 메모 작성 애플리케이션을 만들겠습니다.
개요
지시문은 새로운 구문을 도입하는 것입니다. 지시어는 DOM 요소에 만들어지고 일부 특정 동작과 함께 첨부되는 표시입니다. 예를 들어 정적 HTML은 날짜 선택기 위젯을 만들고 표시하는 방법을 모릅니다. 이 새로운 구문을 HTML에 가르치려면 지시문이 필요합니다. 이 지시문은 날짜 선택기 역할을 하는 요소를 생성합니다. 나중에 이 지시어를 구현하는 방법을 살펴보겠습니다.
이전에 Angular 애플리케이션을 작성한 적이 있다면 인식 여부에 관계없이 이미 지시문을 사용한 것입니다. ng-model, ng-repeat, ng-show 등과 같은 지시어를 사용했을 수도 있습니다. 이러한 지시문은 모두 특정 기능을 DOM 요소에 바인딩합니다. 예를 들어 ng-repeat는 특정 요소를 반복하고 ng-show는 조건부로 요소를 표시합니다. 드래그 가능한 요소를 생성하려면 지시어를 생성해야 할 수도 있습니다. 지시문의 기본 아이디어는 간단합니다. 이벤트 리스너를 요소에 연결하고 DOM을 변환하여 HTML을 대화형으로 만듭니다.
jQuery 관점에서 지시문 보기
jQuery를 사용하여 날짜 선택기를 만드는 방법을 생각해 보세요. 먼저 HTML에 일반 입력 필드를 추가한 다음 jQuery에서 $(element).dataPicker()를 호출하여 날짜 선택기로 변환합니다. 하지만 생각해 보세요. 디자이너가 이 마크업을 조사하고 싶을 때 이 필드가 무엇인지 즉시 추측할 수 있습니까? 그냥 일반 입력 필드인가요, 아니면 날짜 선택기인가요? 이를 확인하려면 jQuery를 살펴봐야 합니다. Angular의 접근 방식은 지시문을 사용하여 HTML을 확장하는 것입니다. 따라서 날짜 선택 지시문은 다음과 같을 수 있습니다:
<date-picker></date-picker>
또는 다음과 같습니다:
<input type='text' data-picker/>
UI 컴포넌트를 만드는 이 방법은 직관적이고 명확합니다. 요소를 보고 그 목적을 알 수 있습니다.
사용자 지정 지시어 만들기
Angular 지시어는 네 가지 형식으로 나타날 수 있습니다.
1. 새로운 HTML 요소(8ea3043659788eba89a22cfe1e88c5f378d3e32b669744ae43f48110828a99d5 )
2. 요소의 속성(fd11e5d445701a76c4913bffa151024c)
3. ='date-picker'/>)
4. 댓글로 (3c2a2819a37b702cc6cffcb7f91fe722)
물론 우리는 완전히 당신입니다. 지침이 HTML에 표시되는 방식을 결정할 수 있습니다. 이제 일반적인 Angular 지시문이 어떻게 작성되는지 살펴보겠습니다. 이는 컨트롤러 등록 방법과 유사하지만 구성 지시문의 일부 속성을 포함하는 간단한 개체(지시문 정의)를 반환합니다. 다음 코드는 간단한 Hello World 지시문을 보여줍니다.
var app = angular.module('myapp',[]); app.directive('helloWorld',function(){ return { restrict: 'AE', replace: true, template: '<h3>Hello World!</h3>' } });
위 코드에서 app.diretive() 함수는 우리 모듈에 있습니다. 명령. 이 함수의 첫 번째 매개변수는 명령의 이름입니다. 두 번째 매개변수는 지시문 정의 객체를 반환하는 함수입니다. 지시문에 $rootScope, $http 또는 $compile과 같은 추가 개체/서비스에 대한 종속성이 있는 경우 해당 지시문도 거기에 삽입될 수 있습니다. 이 지시어는 다음과 같이 HTML 요소로 사용될 수 있습니다:
<hello-world/>
또는:
<hello:world/>
또는 속성으로 사용:
<div hello-world></div>
또는:
<div hello:world/>
HTML5와 호환되도록 하려면 속성 앞에 x- 또는 data- 접두사를 추가하면 됩니다. 따라서 다음 태그는 helloWorld 지시어와 일치합니다:
<div data‐hello‐world></div>
또는
<di vx‐hello‐world></div>
참고
지시문을 일치시킬 때 Angular는 요소/속성 이름에서 접두사 x- 또는 data-를 제거합니다. 그런 다음 구분 기호 - 또는 :를 등록된 지시어와 일치하는 camelCase 표기법으로 변환합니다. 이것이 helloWorld 지시문이 HTML에서 사용될 때 실제로 hello-world로 작성되는 이유입니다.
위의 간단한 명령은 일부 정적 텍스트를 표시하지만 여전히 탐색할 가치가 있는 몇 가지 흥미로운 점이 있습니다. 이 지시문 정의 객체에는 세 가지 속성을 사용했습니다. 이 세 가지 속성이 어떤 용도로 사용되는지 살펴보겠습니다.
restrict - 이 속성은 HTML에서 지시어를 사용하는 방법을 지정합니다(지시어는 네 가지 방식으로 나타날 수 있다는 점을 기억하세요). 이 예에서는 'AE'로 설정했습니다. 따라서 이 지시어는 HTML 요소나 속성으로 사용될 수 있습니다. 지시문을 클래스로 사용하려면 제한을 'AEC'로 설정할 수 있습니다.
template - 这个实行指明了当指令被Angular编译和链接时生成的HTML标记。它不一定是一个简单的字符串。template可以很复杂,其中经常会涉及其它的指令,表达式({{}}),等等。在大多数情况下你可能会想要使用templateUrl而不是template。因此,理想情况下你应该首先将模板放置在一个单独的HTML文件中然后让templateUrl指向它。
replace - 这个属性指明了是否生成的模板会代替绑定指令的元素。在前面的例子中我们在HTML中使用指令为edd05d9b720c954eda8df66606fc7d416d95f50462131a27fde32c4d9df5bdd4,并将replace属性设置为true。因此,在指令编译后,生成的模板代替了edd05d9b720c954eda8df66606fc7d416d95f50462131a27fde32c4d9df5bdd4。最后的输出结果是684271ed9684bde649abda8831d4d355Hello World!39528cedfa926ea0c01e69ef5b2ea9b0。如果你将replace设置为false,默认情况下,输出模板将会被插入到指令被调用的元素中。
link函数和作用域
有一个指令生成的模板是没有用的除非它在正确的作用域中北编译。默认情况下一个指令并不会得到一个新的子作用域。然而,它可以得到父作用域。这意味着如果一个指令位于在一个控制器中那么它将使用控制器的作用域。
为了利用作用域,我们可以使用一个叫做link的函数。它可以通过指令定义对象中的link属性来配置。我们现在对helloworld指令做一些修改一遍当用户在一个input字段中输入一个颜色名称时,Hello Wolld文字的背景颜色会自动发生改变。同样,当一个用户点击Hello World文字时,背景颜色会重置为白色。相应的HTML标记如下所示:
<body ng-controller='MainCtrl'> <input type='text' ng-model='color' placeholder='Enter a color' / > <hello-wolrd/> </body>
修改后的helloWorld指令代码如下所示:
app.directive('helloWorld',function(){ return { restrict: 'AE', replace: true, template: '<p style="background-color:{{color}}"></p>', link: function(scope,elem,attr){ elem.bind('click',function(){ elem.css('background-color','white'); scope.$apply(function(){ scope.color = "white"; }); }); elem.bind('mouseover',function(){ elem.css('cursor','pointer'); }); } } });
注意到link函数被用在了指令中。它接收三个参数:
scope - 它代表指令被使用的作用域。在上面的例子中它等同于符控制器的作用域。
elem - 它代表绑定指令的元素的jQlite(jQuery的一个自己)包裹元素。如果你在AngularJS被包含之前就包括了jQuery,那么它将变成jQuery包裹元素。由于该元素已经被jQuery/jQlite包裹,我们没有必要将它包含在$()中来进行DOM操作。
attars - 它代表绑定指令的元素上的属性。例如,如果你在HTML元素上有一些指令形式为:5525e9281c68be71aaa8c5ced5bd1dc56d95f50462131a27fde32c4d9df5bdd4,你可以在link函数内用attrs.someAttribute来引用这些属性。
link函数主要是用来对DOM元素绑定事件监听器,监视模型属性变化,并更新DOM。在前面的指令代码中,我们绑定了两个监听器,click和mouseover。click处理函数重置了
的背景颜色,而mouseover处理函数则将游标改变为pointer。模板中拥有表达式{{color}},它将随着父作用域中的模型color的变化而变化,从而改变了Hello World的背景色。
Compile函数
Compile函数主要用来在link函数运行之前进行一些DOM转化。它接收下面几个参数:
tElement - 指令绑定的元素
attrs - 元素上声明的属性
这里要注意compile不能够访问scope,而且必须返回一个link函数。但是,如果没有compile函数以依然可以配置link函数。compile函数可以被写成下面的样子:
app.directive('test',function(){ return { compile: function(tElem,attrs){ //在这里原则性的做一些DOM转换 return function(scope,elem,attrs){ //这里编写link函数 } } } });
大多数时候,你仅仅只需要编写link函数。这是因为大部分指令都只关心与注册事件监听器,监视器,更新DOM等等,它们在link函数中即可完成。像是ng-repeat这样的指令,需要多次克隆并重复DOM元素,就需要在link函数运行之前使用compile函数。你可能会问威慑呢么要将两个函数分别使用。为什么我们不能只编写一个函数?为了回答这个问题我们需要理解Angular是如何编译指令的!
指令是如何被编译的
当应用在启动时,Angular开始使用$compile服务解析DOM。这项服务会在标记中寻找指令然后将它们各自匹配到注册的适龄。一旦所有的指令都已经被识别完成,Angular就开始执行它们的compile函数。正如前面所提到的,compile函数返回一个link函数,该函数会被添加到稍后执行的link函数队列中。这叫做编译阶段(compile phase)。注意到即使同一个指令有几个实例存在,compile函数也只会运行一次。
在编译阶段之后就到了链接阶段(link phase),这时link函数就一个接一个的执行。在这个阶段中模板被生成,指令被运用到正确的作用域,DOM元素上开始有了事件监听器。不像是compile函数,lin函数会对每个指令的实例都执行一次。
改变指令的作用域
默认情况下指令应该访问父作用域。但是我们并不像对所有情况一概而论。如果我们对指令暴露了父控制器的scope,那么指令就可以自由的修改scope属性。在一些情况下你的指令可能想要添加一些只有内部可以使用的属性和函数。如果我们都在父作用域中完成,可能会污染了父作用域。因此,我们有两种选择:
一个子作用域 - 这个作用域会原型继承父作用域。
一个隔离的作用域 - 一个全新的、不继承、独立存在的作用域。
作用域可以由指令定义对象中的scope属性定义。下面的例子展示了这一点:
app.directive('helloWorld',function(){ return { scope: true, //使用一个继承父作用域的自作用域 restrict: 'AE', replace: true, template: '<h3>Hello World!</h3>' } });
上面的代码要求Angular为指令提供一个能够原型继承父作用域的子组用于。另一种情形,一个隔离作用域,代码如下所示:
app.directive('helloWorld',function(){ return { scope: {}, //使用一个全新的隔离作用域 restrict: 'AE', replace: true, template: '<h3>Hello World!</h3>' } });
上面的指令使用一个不继承父作用域的全新隔离作用域。当你想要创建一个可重用的组件时隔离作用域是一个很好的选择。通过隔离作用域我们确保指令是自包含的兵可以轻松地插入到任何HTML app中。这种做法防止了父作用域被污染,由于它不可访问父作用域。在我们修改后的helloWorld指令中如果你将scope设置为{},那么代码就不会再正常运行。它将创建一个隔离的作用域然后表达式{{color}}将无法引用隔离作用域中的属性因此值变为undefined。
隔离作用域并不意味着你一点都不能获取到父作用域中的属性。有一些技巧可以使你访问父作用域中的属性同时监听这些属性的变化。我们将在后续文章中提到这种高级技巧。