-----------------------------------------------------------------------------------

原文:https://www.sitepoint.com/practical-guide-angularjs-directives/

A practical guide to angularJS directives.

Directives are the most important components of any AngularJS application. Although AngularJS ships with wide range of directives, you will often need to create application specific directives. This tutorial will give an overview of custom directives and explain how to use them in real world Angular projects. At the end of the tutorial, I will guide you through the creation of a simple note taking app with the help of Angular directives.

Overview

A directive is something that introduces new syntax. Directives are markers on a DOM element which attach a special behavior to it. For example, static HTML does not know how to create and display a date picker widget. To teach HTML this new syntax we need a directive. The directive will somehow create an element that behaves like a date picker. We will see how this is achieved subsequently.

If you’ve written an Angular application before, then you have used directives, whether you realized it or not. You might have used simple directives like ng-model,ng-repeat,ng-show, etc. All these directives attach special behavior to DOM elements. For example, ng-repeat repeats a specific element and ng-show conditionally shows an element. If you want to make an element draggable/droppable you might create a directive for that too. The basic idea behind directive is very simple. It makes your HTML truly interactive by attaching event listeners to the elements and/or transforming the DOM.

The jQuery Perspective

Just imagine how you create a date picker with jQuery. We first add a normal input field to the HTML and then in jQuery we call $(element).datePicker() to actually convert it to a date picker. But, think about it. When a designer comes and examines the markup, can he/she immediately guess what the field actually is? Is it just a plain input field or a date picker? You have to look into the jQuery code to be sure. The Angular approach is to use a directive to extend HTML. So, a directive for a date picker can look like this:

<date-picker></date-picker>

Or it could look like this:

<input type="text" date-picker/>

This approach to creating UI components is intuitive and clear. You can simply look at the element to know what it is supposed to be!

Building Custom Directives:

An Angular directive comes in four flavors in terms of appearance.

  1. A new HTML element (<date-picker></date>).
  2. An attribute on an element (<input type="text" date-picker/>).
  3. As a class (<input type="text" class="date-picker"/>).
  4. As comment (<!--directive:date-picker-->).

Of course, we have control over how our directive will appear in the HTML. Now, let’s see how a typical directive is written in Angular. It is registered in the same way as a controller, but it returns a simple object (directive definition) that has several properties to configure the directive. The following code shows a simple Hello World directive.

var app = angular.module('myapp', []);

app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});

In the previous code, the app.directive() function registers a new directive in our module. The first argument to this function is the directive name. The second argument is a function which returns a directive definition object. If your directive has dependencies on external objects/services such as $rootScope$http, or $compile, they can be injected here. The directive can be used in HTML as an element like this:

<hello-world/>

Or:

<hello:world/>

Or, as an attribute like this:

<div hello-world></div>

Or:

<div hello:world/>

If you want to be HTML5 compliant, you can prefix the attribute name with x- or data-. So, the following markups will match helloWorld directive.

<div data-hello-world></div>

OR

<div x-hello-world></div>

Note:

While matching directives, Angular strips the prefix x- or data- from element/attribute names. Then it converts - or :delimited strings to camelCase and matches with the registered directives. That’s why we have used the helloWorlddirective as hello-world in the HTML.

Though the above directive does nothing more than show static text, we have some interesting points to explore. We have used three properties in the directive definition object to configure the directive. Let’s explore the role that each one plays.

  • restrict – This provides a way to specify how a directive should be used in HTML (remember a directive can appear in four ways). In this case we have set it to 'AE'. So, the directive can be used as a new HTML element or an attribute. To allow this directive to be used as a class we can set restrict to 'AEC'.
  • template – This specifies the HTML markup that will be produced when the directive is compiled and linked by Angular. This does not have to be a simple string. The template can be complex, often involving other directives, expressions ({{ }}), etc. In most cases you want to use templateUrl instead of template. So, ideally you should place the template in a separate HTML file and make templateUrl point to it.
  • replace – This specifies if the generated template will replace the HTML element on which the directive is attached. In our case we have used the directive as <hello-world></hello-world>, and replace is set to true. So, after the directive is compiled, the produced output template replaces <hello-world></hello-world>. The final output is <h3>Hello World!!</h3>. If you set replace to false, the default, the output template will be inserted into the element on which the directive is invoked.

Open up this plunker, right click on “Hello World!!”, and do an inspect element to visualize things.

The link Function and Scope

The template produced by a directive is meaningless unless it’s compiled against the right scope. By default a directive does not get a new child scope. Rather, it gets the parent’s scope. This means that if the directive is present inside a controller it will use that controller’s scope.

To utilize the scope, we can make use of a function called link. This is configured by the link property of the definition object. Let’s change our helloWorld directive so that when the user types a color name into an input field, the background color of Hello World text changes automatically. Also, when a user clicks on the text Hello World, the background color should reset to white. The HTML markup is shown below.

<body ng-controller="MainCtrl">
<input type="text" ng-model="color" placeholder="Enter a color" />
<hello-world/>
</body>

The modified helloWorld directive is shown below:

app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: true,
template: '<p style="Hello World',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
elem.css('background-color', 'white');
scope.$apply(function() {
scope.color = "white";
});
});
elem.bind('mouseover', function() {
elem.css('cursor', 'pointer');
});
}
};
});

Notice the link function used in the directive. It takes three arguments:

  • scope – The scope passed to the directive. In this case it’s the same as the parent controller scope.
  • elem – The jQLite (a subset of jQuery) wrapped element on which the directive is applied. If you have included jQuery in the HTML before AngularJS is included, this becomes jQuery wrapped instead of jQLite. As the element is already wrapped with jQuery/jQLite, there is no need to again wrap it inside $() for DOM manipulations.
  • attrs – An object representing normalized attributes attached to the element on which the directive is applied. For example, you can attach attributes in HTML like: <hello-world some-attribute></hello-world> and access it in the link function as attrs.someAttribute.

The link function is mainly used for attaching event listeners to DOM elements, watching model properties for changes, and updating the DOM. In the previous directive snippet, we attached two listeners, click and mouseover. The click handler resets the background color of the <p>, while the mouseover handler changes the cursor to pointer. The template has an expression {{color}} which changes whenever the model color changes in the parent scope, thereby changing the background color of Hello World. Here is a plunker demonstrating this concept.

The compile Function

The compile function is used to perform any DOM transformation before the link function runs. It accepts the following arguments.

  • tElement – The element on which the directive is applied.
  • attrs – The normalized list of attributes declared on the element.

Just note that the compile function does not have access to the scope, and must return a link function. But, if there is no compile function you can configure the link function as usual. The compile function can be written as:

app.directive('test', function() {
return {
compile: function(tElem,attrs) {
//do optional DOM transformation here
return function(scope,elem,attrs) {
//linking function here
};
}
};
});

Most of the time, you will be working with the link function only. This is because most of the directives are concerned with registering event listeners, watchers, updating the DOM, etc., which are done inside the link function. Directives like ng-repeat, which need to clone and repeat the DOM element several times, use the compile function before the link function runs. This leads to a question of why two separate functions are needed. Why can’t we have just one? To answer this we need to understand how directives are compiled by Angular!

How Directives are Compiled

When the application bootstraps, Angular starts parsing the DOM using the $compile service. This service searches for directives in the markup and matches them against registered directives. Once all the directives have been identified, Angular executes their compile functions. As previously mentioned, the compile function returns a link function which is added to the list of link functions to be executed later. This is called the compile phase. If a directive needs to be cloned multiple times (e.g. ng-repeat), we get a performance benefit as the compile function runs once for the cloned template, but the link function runs for each cloned instance. That’s why the compile function does not receive a scope.

After the compile phase is over the linking phase, where the collected link functions are executed one by one, starts. This is where the templates produced by the directives are evaluated against correct scope and are turned into live DOM which react to events!

Changing a Directive’s Scope

By default a directive gets the parent’s scope. But we don’t want that in all cases. If we are exposing the parent controller’s scope to the directives, they are free to modify the scope properties. In some cases your directive may want to add several properties and functions to the scope that are for internal use only. If we are doing these things to parent’s scope, we are polluting it. So, we have two other options:

  • A child scope – This scope prototypically inherits the parent’s scope.
  • An isolated scope – A new scope that does not inherit from the parent and exists on its own.

The scope can be configured with the scope property of the directive definition object. An example of this is shown in the following snippet.

app.directive('helloWorld', function() {
return {
scope: true, // use a child scope that inherits from parent
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});

The above code asks Angular to give the directive a new child scope that prototypically inherits from parent scope. The other option, an isolated scope, is shown below.

app.directive('helloWorld', function() {
return {
scope: {}, // use a new isolated scope
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});

This directive uses a new isolated scope that does not inherit from the parent. Isolated scopes are good when we want to create reusable components. By isolating the scope we guarantee that the directive is self contained and can be easily plugged into an HTML app. This provides the parent scope from becoming polluted, as it is not accessible inside the directive. In our modified helloWorld directive if you set scope to {} the code won’t work anymore. It will create an isolated scope for the directive and the expression {{color}} will now refer to that isolated scope property (not parent scope) which is undefined.

Isolating the scope does not mean that you have no access to the parent scope’s properties. There are techniques that allow you to access the parent scope’s properties and also watch for changes on them. We will discuss these techniques, and some more advanced concepts like Controller functions in part two of this series. Part two will also walk you through the creation of a fully fledged note taking app using Angular directives, so stay tuned!

angularJS 系列(七)---指令的更多相关文章

  1. AngularJS中Directive指令系列 - scope属性的使用

    文章是转的,我做下补充.原文地址:https://segmentfault.com/a/1190000002773689 每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部 ...

  2. AngularJS 系列 01 - HelloWorld和数据绑定

    目录导读: AngularJS 系列 学习笔记 目录篇 前言: 好记性不如烂键盘,随笔就是随手笔记,希望以后有用. 本篇目录: 1. Hello World 2. AngularJS中的数据绑定 3. ...

  3. 带你走近AngularJS - 创建自定义指令

    带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自定义指令 ------------- ...

  4. AngularJS系列之总结

    AngularJS深入的系列就是这九篇博客了,把我以前在项目中应用到的和自己学习的都总结在了里面.为了更方便的看,把我写的AngularJS系列的博客都列到下面.之后就开始学习ionic:html5移 ...

  5. AngularJS 系列 学习笔记 目录篇

    目录: AngularJS 系列 01 - HelloWorld和数据绑定 AngularJS 系列 02 - 模块 (持续更新)

  6. AngularJS 系列 02 - 模块

    引导目录: AngularJS 系列 学习笔记 目录篇 前言: 其实,在上篇文章介绍数据绑定的时候,我们的HelloWorld的代码案例中就已经使用了模块(module).哈哈. 本篇就着重介绍一下a ...

  7. AngularJS中的指令全面解析(转载)

    说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方.双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJ ...

  8. 使用Angularjs的ng-cloak指令避免页面乱码

    在使用Anguarjs进行web开发或者进行SPA(single page application)开发时,往往会遇到下面这样的问题. 刷新页面时,页面会出现一些乱码,这里的乱码具体是指`{{expr ...

  9. WCF编程系列(七)信道及信道工厂

    WCF编程系列(七)信道及信道工厂   信道及信道栈 前面已经提及过,WCF中客户端与服务端的交互都是通过消息来进行的.消息从客户端传送到服务端会经过多个处理动作,在WCF编程模型中,这些动作是按层 ...

  10. 你知道用AngularJs怎么定义指令吗?

    前言 最近学习了下angularjs指令的相关知识,也参考了前人的一些文章,在此总结下. 欢迎批评指出错误的地方.   Angularjs指令定义的API AngularJs的指令定义大致如下 ang ...

随机推荐

  1. 【NOIP2013提高组】货车运输

    货车运输  (truck.cpp/c/pas) [问题描述]  A国有n座城市,编号从1到n,城市之间有m条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有q辆货车在运输货物,司机们想知道每辆 ...

  2. vue跨组件通信的几种方法

    http://www.tuicool.com/articles/jyM32mA 在开发组件的时候,一定会遇到组件的通信,比如点击一个图标出现弹窗和蒙层,这三个分别是不同的组件.管理他们之间的状态就成了 ...

  3. 为什么重写equals()必须重写hashCode()

    主要原因是因为hashCode是用对象的内部地址转换成一个整数的,而equals比较得是两个对象,或者是两个对象的内容 如果你重写了equals,而保留hashCode的实现不变,那么很可能两个对象明 ...

  4. iOS 导航栏 不透明

    UINavigationBar.appearance().translucent = false UINavigationBar.appearance().barStyle = UIBarStyle. ...

  5. python文件操作_对文件进行复制拷贝_代码实现

    要求: 1,对已经存在的文件进行复制操作 2,复制后的文件在文件名后面加上[复件] 3,文件比较大如何优化处理 #-*- coding: UTF-8 -*- #这是python 2 下面写的,用的ra ...

  6. HDU-1757--A Simple Math Problem(矩阵乘法)

    Problem Description Lele now is thinking about a simple function f(x).If x < 10 f(x) = x.If x > ...

  7. Snacks

    Snacks 题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5692 dfs序+线段树 这道题涉及到对整棵树的值修改,考虑将树状结构用dfs ...

  8. AUC计算方法总结

    一.roc曲线 1.roc曲线:接收者操作特征(receiveroperating characteristic),roc曲线上每个点反映着对同一信号刺激的感受性. 横轴:负正类率(false pos ...

  9. 关于 pace 有意思的一篇文章

    http://www.blogjava.net/xingcyx/archive/2006/12/28/90498.html http://blog.sina.com.cn/s/blog_700a8db ...

  10. String类之indexOf--->查找某字对应的位置

    以下方法都是java内置类String类的内置方法(不是构造方法哦,就是普通的方法),不需要我们写,直接拿过来用即可. indexOf方法对应Api介绍 lastIndexOf方法对应Api介绍 -- ...