点击查看AngularJS系列目录
转载请注明出处:http://www.cnblogs.com/leosx/


 

什么是指令

注:本指南是针对已经熟悉AngularJS基础知识的开发人员。如果你才刚刚开始,我建议查看系列教程。(这章是Angular的重点)

指令是一个Dom元素上的标签(和元素上的属性, CSS 类样式一样,属于这个Dom元素),它告诉AngualrJS的HTML 编译器($compile),去附加一个行为到这个Dom元素上去,这个行为可以改变这个Dom元素,或者这个Dom元素的子元素。

AngularJS 有一套自己内置的指令,如:ngBind,ngModel,ngClass等等...你也可以自定义自己的指令。当Angular应用程序起来之后,会先去加载Dom树,然后去匹配你的指令,然后执行。

HTML 编译器 -- 对于Angularjs来说,编译意味着递归去给HTML添加一些事件监听,让DOM元素和Angualr之间可以进行交互,相互作用。这里使用编译这个术语的原因是:添加事件监听这个过程,刚好反应了我们C#,Java等编程语言将源代码编译成应用的这个过程。

 

匹配指令

在我们编写我们的第一个自定义指令之前,我能需要知道AngularJS的HTML编译器是怎么确定在什么时候使用我们自定义的指令的。

下面这个例子中,我们可以看到<Input>元素匹配到了 ngModel 这个指令。

<input ng-model="foo">

下面这个例子也是匹配到了 ngModel 这个指令:

<input data-ng:model="foo">

AngularJS使用元素标签类型(eg:input)和属性名称来确定哪个元素匹配到了哪个指令。标准的指令(eg: ngModel)是区分大小写的,使用了驼峰命名法则。然而,由于HTML是不区分大小写的,所以后来也不区分大小写了…(感觉等于没说…) 但是通常我们都是使用  [-]  这个破折号来代表一个指令(通常,并不是必须)。

一般的使用方法如下:

1.使用  x-  或者  data-  在元素或者属性前。

2.使用  : 或者  - 或者 _ 来代替了驼峰命名法则。

<div ng-app="docsBindExample">
<div ng-controller="Controller">
Hello <input ng-model='name'> <hr/>
<span ng-bind="name"></span> <br/>
<span ng:bind="name"></span> <br/>
<span ng_bind="name"></span> <br/>
<span data-ng-bind="name"></span> <br/>
<span x-ng-bind="name"></span> <br/>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsBindExample', [])
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)';
}]);
})();
</script>

效果图:

$compile编译器会根据元素名称,属性,CSS类名,甚至是注释去匹配指令

下面展示了一个指令可以再哪些地方被引用:

<my-dir></my-dir>
<span my-dir="exp"></span>
<!-- directive: my-dir exp -->
<span class="my-dir: exp;"></span>

建议1:  最好的使用指令方式是通过标签名,属性名,而不是使用注释和CSS样式类名。这样做可以让编译器更加容易的去正确匹配指令。

建议2:  注释指令通常用来解决Dom API的限制,进行跨越多个元素的操作。比如在 <Table> 元素内,去跨越多个元素进行操作。Angular1.2中引入了ng-repeat-startng-repeat-end指令,它们可以更好的去解决这个问题。鼓励开发者使用它们,而不是使用注释指令去解决这样的问题。

 

文本和属性绑定

在编译过程当中,编译器会根据文本的属性值和使用了$interpolate服务的属性去查看这个HTML元素是否包含嵌入式的表达式。如果有,那么这些表达式会被注册到监视器中,并且会进入$digest阶段。下面是一个使用了$interpolate 表达式的示例:

<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>

 

ngAttr属性绑定

我们的浏览器有时候会挑剔什么是他们认为又要的属性值(也就是属性值不是你随便写的) 想想看下面的例子:

<svg>
<circle cx="{{cx}}"></circle>
</svg>

我们很希望Angular允许我们这样绑定,但是当我们打开浏览器控制台的时候,你就会看到如下错误:

Error: Invalid value for attribute cx="{{cx}}".

这是因为SVG DOM API的限制原因,你就不能写成: cx=”{{cs}}”了,而应该使用: ng-attr-cx 来解决这个问题。

如果一个属性的绑定的前缀是 ngAttr 而不是正规的 ng-attr-这种形式,那么在binding期间,它会被应用到相应的没有前缀的属性上去(eg: <circle ngAttr-cx=”a”></circle> 它是不会被应用到cx属性上去的)。因为当使用ngAttr前缀的时候,对于$interpolate阶段来说,任何标识都还不起作用。所以在这个阶段中所有的string结果都是 undefined的,既然是undefined的,那么对应的这个属性会被移除,而不再继续添加到元素上去。

我们可以如下去解决上面提到的问题:

<svg>
<circle ng-attr-cx="{{cx}}"></circle>
</svg>

 

注册指令

我们先来谈谈和注册指令相关的API,和controller一样,指令也是被注册到模块(modules)上的,创建指令的API是: module.directive(指令名称,功能函数)。module.directive(direName,func)  ,这个func方法需要返回一个 配置参数对象,通常是一个Json对象,来告诉$compile编译器,进行这个指令匹配时的行为及动作.

这个func功能函数只会在当编译器第一次匹配到指令的时候执行一次。你可以在这里执行任何的初始化工作。这个func函数是被 $injector.invoke方法调用的。

友情提示:通常,为了避免与未来一些指令冲突,我们自定义的时候,都最好有一个自己的前缀。。。

 

模板扩展指令

我们想想,如果你现在需要一个模板,去展示客户的信息,而且这个模板会重复出现很多次,当你要改变一个地方,你就得去改很多次。这个是一个很好的情景让你去使用指令(directive)去简化你的模板(你该使用模板扩展指令了)。

下面,我们就是用一个例子来说明:

<div ng-app="docsSimpleDirective">
<div ng-controller="Controller">
<div my-customer></div>
</div>
<div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
})();
</script>

效果图:

当$compile编译器编译,并且连接了<div my-customer></div>之后,它将会继续尝试匹配这个元素的子元素。这就意味着你可以篡改其它指令哦!一会儿我们来看下面的例子。

还有需要注意的,上面的例子当中,我们返回了参数 template,当模板大小改变的时候,这是很烦人的哦!你的字符串会很长,很长! 哈哈!好像邪恶了。 除非你的模板就像上面例子一样,确实特别小,否则,我们还是建议你把模板吓到一个单独的HTML文件当中去,然后再指令的func中,我们返回的参数中,使用templateUrl选项,来指定我们的模板路径。

<div>
<div ng-controller="Controller">
<div my-customer></div>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
templateUrl: 'my-customer.html'
};
});
})();
</script>

my-customer.html的内容:

Name: {{customer.name}} Address: {{customer.address}}

效果图:

其实,templateUrl选项也可以是一个函数,返回要加载的HTML模板的URL路径,Angular会自动调用templateUrl函数,并且传入两个参数:ele(对应的元素), 和一个与这个元素相关的attr对象。

注意:在scope没有初始化之前,你是不能够在templateUrl函数中访问scope的,所以说,Controller的初始化一定是要在使用templateUrl函数之前。

<div ng-app="docsTemplateUrlDirective">
<div ng-controller="Controller">
<div my-customer type="name"></div>
<div my-customer type="address"></div>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
templateUrl: function(elem, attr){
return 'customer-'+attr.type+'.html';
}
};
});
})();
</script>

customer-name.html

Name: {{customer.name}}

customer-address.html

Address: {{customer.address}}

效果图:

 

再次注意:默认情况下,你创建的指令仅限于用于属性或者元素,为了创建一个可以用于类(class)的触发器,你需要使用到restrict选项。

restrict选项的几个经典选项:

‘A’ -  仅仅匹配属性名

‘E’ -   仅仅去匹配元素名

‘C’ -   仅仅匹配class名

友情提示:这些选项可以根据需要进行组合的哦!!

‘AEC’- 它可以匹配属性(attr)或者元素(ele)或者类名(class)。

 

我们来写一个例子,让我们的这个指令只能去匹配元素名,也就是说这个指令只能用于元素上。

<div ng-app="docsRestrictDirective">
<div ng-controller="Controller">
<my-customer></my-customer>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsRestrictDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
restrict: 'E', // 注意这里哦!
templateUrl: 'my-customer.html'
};
});
})();
</script>

my-customer.html内容如下:

Name: {{customer.name}} Address: {{customer.address}}

效果图:

更多的关于restrict选项,你可以参考API文档(你知道的,有可能访问不了哦!肿么破,这里不说哈!)

 

更大的问题来了。什么时候我该用属性,什么时候我们该用元素呢?  不出意外,其实你应该能知道,什么时候该用什么。 当我们要创建一个控件的时候,那我们就得使用元素匹配方式。当你需要给你现在的元素增加一些新功能,进行修饰,扩展的时候,我们就得使用属性了。不多解释…

 

指令独立的scope

大家可以想一想,上面说的关于指令的使用的例子,就会发现它有个致命的缺陷,一个Scope(也就是一个Controller)里面你只能使用一次我们自定义的myCustomer指令。这样的话,我们如果想要重复的使用这个指令的时候,你就得声明与之对应个数的Controller,Oh~~ my god  !!!  你肯定瞬间不想使用Angular了,不错不要着急,会有解决方法的。

 

我们可以使用给每个directive(指令)一个独立的scope来进行分离,然后将外部Controller中的scope映射到directive(指令)内部的独立的scope上去。这样,我们就可以调用自己独立的scope啦! 是不是很好哦!要实现上面的独立,AngularJS给我们提供了一个directive的scope参数,看下面示例,你就明白了!

<div ng-app="docsIsolateScopeDirective">
<div ng-controller="Controller">
<my-customer info="naomi"></my-customer>
<hr>
<my-customer info="igor"></my-customer>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsIsolateScopeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.igor = { name: 'Igor', address: '123 Somewhere' };
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
templateUrl: 'my-customer-iso.html'
};
});
})();
</script>

my-customer-iso.html内容如下:

Name: {{customerInfo.name}} Address: {{customerInfo.address}}

效果图:

我们来解释一下上面,在<my-customer>元素中,我们给它的info属性中,绑定了Controller中的Scope中的naomi属性上去了。 第二个<my-customer>元素的info属性我们是给绑定了igor属性。

我们来仔细看看scope选项:

// ...
scope:{
customerInfo: '=info'
},
// ...

 

scope选项中,包涵了所有关于这个指令独立的scope信息,在上面例子中,它只包涵了一个属性:

customerInfo 是这个指令独立的scope的属性名,它的值为  =info   ,这是在告诉$compile编译器,去把这个属性绑定到一个叫做info的属性上去。。。

注意:上面的绑定是一个很标准的绑定方式,但是如果你想绑定成这样:<div bind-to-this=”thing”>,你需要将customerInfo的值设置为  =bindToThis    (也就是驼峰命名法则)

我们来考虑还有一种情况,如果说你想就让绑定的属性名和你这个指令独立的scope的属性名一样的话,你可以使用下面这个简写语法:

...
scope: {
// same as customer: '=customer'
customer: '='
},
...

 

除了可以将不同的数据绑定到directive 独立的scope上之外,使用独立的scope还有另外一个效果.

不解释,先来段代码:

<div ng-app='docsIsolationExample'>
<div ng-controller="Controller">
<my-customer info="naomi"></my-customer>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsIsolationExample', [])
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
templateUrl: 'my-customer-plus-vojta.html'
};
});
})();
</script>

my-customer-plus-vojta.html 内容如下:

Name: {{customerInfo.name}} Address: {{customerInfo.address}}
<hr>
Name: {{vojta.name}} Address: {{vojta.address}}

效果图如下:

 

请注意: {{vojta.name}} 和 {{vojta.address}} 值是空的。也就是他们根本没有被定义。虽然我们在controller中的scope中定义了vojta属性,但是,它在我们自定义的directive(指令)中,是不可用的。。。

顾名思义,在这个directive的独立scope中,除了实体模型你需要明确的增加之外,你可以添加任何的东西到这个独立的scope中,那么这样的话,我们就可以在这里进行重构组件,这样的话,我们就可以防止控件改变了你的模型的状态,当然,除了你明确的指定将这个模型传入进来了,你都传进来了,就可以改变了。

注意:在一般情况下,一个scope是从上级的scope派生来的,但是这里是一个特例哦!  directive(指令)中的scope不是那样的,如果你想要了解更多的关于定义指令的对象- scope 你可以看看这里。

建议: 当我们想要让我们自定义的组件在你的整个Angular应用程序中重用的话,你就要使用独立的scope,但是,如果你只使用一次的话,就可以不用了。不过个人建议,最好还是使用吧! *^_^*

 

 

创建一个指令,去操作DOM

在这个例子当中。我们去创建一个指令,用于显示当前的时间。一秒更新一次,我们就需要更新DOM去显示当前时间。

通常,我们如果想要在指令中去修改DOM元素的话(Angular不建议你直接去操作DOM),会使用 link 选项 ,link 选项的签名如下:

function link(scope, element, attrs){ ...... }
   [ scope ]是一个Angular的scope对象
   [ element ]是这个指令所匹配到的那个元素。
   [ attrs ] 是一个规范的key-value键值对儿的hash对象

在link 方法中,我们想每秒钟更新一下显示时间, 可以使用Angular自带的 $interval 服务,相比之下,它要比  $timeout 服务更加好用,而且更加的易于端对端测试,它会保证在你的测试完成之前,你的$timeout 里面的所有执行都已完成,而且,它也可以保证当你把directive(指令)移除了之后,会自动的把 $interval 服务给卸载掉,从而不会造成内存的泄露。

是骡子是马,咱们还是直接拉出来溜溜!! 直接上代码!

<div ng-ap="docsTimeDirective">
<div ng-controller="Controller">
Date format: <input ng-model="format"> <hr/>
Current time is: <span my-current-time="format"></span>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsTimeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
}])
.directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) { function link(scope, element, attrs) {
var format,
timeoutId; function updateTime() {
element.text(dateFilter(new Date(), format));
} scope.$watch(attrs.myCurrentTime, function(value) {
format = value;
updateTime();
}); element.on('$destroy', function() {
$interval.cancel(timeoutId);
}); // start the UI update process; save the timeoutId for canceling
timeoutId = $interval(function() {
updateTime(); // update DOM
}, 1000);
} return {
link: link
};
}]);
})();
</script>

看看效果图(自己去实践哈,这里不做jif的动态图了哈):

 

这里有几个需要注意的地方。 就像module.contoller API一样,module.directive函数也是依赖注入的,因此,我们可以注入 $interval 和 dateFilter(过滤器),然后再link 函数中进行使用了。

我们注册了事件 element.on(‘$destory’, … )    在 $destory  事件中,发生了什么事情呢?

在AngularJS里有几个特别的事件。当一个DOM节点在Angular的编译器中要被销毁,会触发 $destory  事件。同样的,当一个AngularJS的 scope 对象被销毁的时候,也会广播一个 $destory 事件,给那些监听scope的对象。

通过监听这个事件,你可以去删除哪些可能会导致内存溢出的监听器。已注册监听的scope和元素(element)会在它们被销毁的时候自动清理掉,我们是不用去手动销毁的。但是,如果你你注册了一个服务,或者一个监听DOM节点的监听器,而且它们还被删除了,那么,你就得自己手动去清理。否则可能会造成内存泄露。

建议:指令应该由他们自己去清理的。也就是说,你需要调用 element.on( ‘$destory’, … ) 或者 scope.$on( ‘$destory’, …)  去运行一个清理函数(func),来保证当这个指令被清除的时候,会清除掉和它相关的资源。

 

我们来创建一个封装了其它元素的指令:

现在你知道了怎么传递一个model到 directive上的独立scope上去。 但是,有的时候,我们需要传递整个模板,而不只是一个字符串或者一个对象而已,那怎么办呢?  假设我们现在需要创建一个对话框组件。对话框应该可以显示任意对象,该怎么实现呢?  这需要用到另外的一个选项:transclude    直接上代码:

<div ng-app="docsTransclusionDirective">
<div ng-controller="Controller">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsTransclusionDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Tobias';
}])
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'my-dialog.html'
};
});
})();
</script>

my-dialog.html页面内容:

<div class="alert" ng-transclude>
</div>

我们来看看效果图:

那这个 transclude 选项做了些什么事情呢?   transclude 选项会让指令去访问来自指令之外的 scope 。而不是去访问自己内部的scope 。

还是使用例子来解释下吧! 下列中,请注意,我们添加了一个 link 函数,它重新设置了scope中的name属性的值为 “Jeff”,你猜猜在 {{name}} 绑定中,会显示什么呢?

<div ng-app="docsTransclusionExample">
<div ng-controller="Controller">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsTransclusionExample', [])
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Tobias';
}])
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});
})();
</script>

my-dialog.html 中的内容如下:

<div class="alert" ng-transclude>
</div>

 

一般来说,我们期望 {{name}} 里面显示的是 “Jeff”,但是,but,事实上,它显示的依然是 “Tobias”。选项 transclude 改变了 scope的嵌套方式,它会让{{name}} 指令去访问指令外的scope,而不是去访问指令内部的scope 。

注意:如果你的指令没有创建自己的scope,那么你再在link 中, scope.name= ‘Jeff’的话,就会看到输出的结果是 “Jeff”了,因为这样相当于重新定义了外部scope的name属性值为 “Jeff”。不要绕晕了哦!

这个对于那些需要包涵一些不同内容(内容可变)的指令来说非常有意义哦! 没有这个特性的话,你就得分别创建多个不同的实例了。

建议: 从我们的使用来看就可以知道, transclude选项的使用情景应该是当你的指令内容中,需要包含经常变化或者是不定的地方。

接下来,我们来创建一个有按钮的对话框,它允许我们去绑定一些我们自定义的行为上去,直接上代码:

<div ng-app="docsIsoFnBindExample">
<div ng-controller="Controller">
<my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
Check out the contents, {{name}}!
</my-dialog>
</div>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsIsoFnBindExample', [])
.controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
$scope.name = 'Tobias';
$scope.hideDialog = function () {
$scope.dialogIsHidden = true;
$timeout(function () {
$scope.dialogIsHidden = false;
}, 2000);
};
}])
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'close': '&onClose'
},
templateUrl: 'my-dialog-close.html'
};
});
})();
</script>

my-dialog-close.html 内容如下:

<div class="alert">
<a href class="close" ng-click="close()">&times;</a>
<div ng-transclude></div>
</div>

再来看看效果图:

当我们点击关闭按钮的时候,它会把下面那句话隐藏掉。两秒钟后,再显示出来,自己亲自去试!

正如我们所知道一样,我们可以在指令的scope中定义一些函数(func),然后在scope的上下文中去调用它,但是注意的是,这个函数并需先定义。

我们之前看到了怎么样在scope选项中使用  =attr 。但是上面的例子当中,我们使用了 &attr 实例。这里的这个 & 绑定(别看只是一个符号,它是一个绑定哦!) 允许指令在原始scope的特定时间,去触发调用一个表达式。这个表达式不限制,任意表达式都行,包括那些包涵了方法,函数调用的表达式。正因如此, & 绑定符,是一个很理想的 绑定 指令的回调函数 的方式。

当用户点击对话框中的X按钮的时候,指令的close 函数将被调用,由于ng-click的执行,事实上,它会调用它指令自己独立的scope,而那个scope选项(是选项哦,不要搞晕了),又去调用了上下文中父级scope的 hideDialog() 方法。因此实现了控制器中的hideDialog功能。

建议: 当你想要给你的自定义指令暴露一个绑定行为的API的时候,你就可以使用scope选项 的 &attr 。

 

创建一个增加事件监听的指令

以前,我们使用 link 选项函数去创建一个指令去操作DOM元素。下面,我们来创建一个能够反映使用它的元素的事件的指令。这个示例中,我们会创建一个可以让元素拖动的指令。

先上例子:

<div ng-app="dragModule">
<span my-draggable>Drag ME</span>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('dragModule', [])
.directive('myDraggable', ['$document', function($document) {
return function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0; element.css({
position: 'relative',
border: '1px solid red',
backgroundColor: 'lightgrey',
cursor: 'pointer'
}); element.on('mousedown', function(event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.pageX - x;
startY = event.pageY - y;
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
}); function mousemove(event) {
y = event.pageY - startY;
x = event.pageX - startX;
element.css({
top: y + 'px',
left: x + 'px'
});
} function mouseup() {
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
};
}]);
})();
</script>

效果图:

 

创建一个用于通信的指令

还是先上例子吧!

<div ng-app="docsTabsExample">
<my-tabs>
<my-pane title="Hello">
<h4>Hello</h4>
<p>Lorem ipsum dolor sit amet</p>
</my-pane>
<my-pane title="World">
<h4>World</h4>
<em>Mauris elementum elementum enim at suscipit.</em>
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
</my-pane>
</my-tabs>
</div>
<script src="https://code.angularjs.org/1.3.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
angular.module('docsTabsExample', [])
.directive('myTabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope) {
var panes = $scope.panes = []; $scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}; this.addPane = function(pane) {
if (panes.length === 0) {
$scope.select(pane);
}
panes.push(pane);
};
},
templateUrl: 'my-tabs.html'
};
})
.directive('myPane', function() {
return {
require: '^myTabs',
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});
})();
</script>

my-tabs.html   内容如下:

<div class="tabbable">
<ul class="nav nav-tabs">
<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">
<a href="" ng-click="select(pane)">{{pane.title}}</a>
</li>
</ul>
<div class="tab-content" ng-transclude></div>
</div>

my-pane.html  内容如下:

<div class="tab-pane" ng-show="selected" ng-transclude>
</div>

效果图:

 

在 myPane 指令中,使用到了一个 require选择,其值为:  ^myTabs  。当一个指令使用了这个选项的, $compile 编译器会需要让你提供一个 contorller ,不然的话,它会抛出一个异常的。  使用 ^  前缀意味着这个指令会在它的父级元素(如果没有 ^ 前缀的话,它会在自己的元素里面查找Controller)里面去查找Controller。

那么,这个 myTabs controller又是从哪里来的呢? 指令可以使用Controller选项去指定controller从哪里来! 就像上面例子一下,  myTabs 指令使用了这个选项。 这个选项的值和AngularJS的控制器( ngController )写法一样,这个选项也可以附加一些模板给这个指令。

如果有必要引用一个controller或者任何的函数,然后将他们绑定到模板的controller的scope上去的话,你可以使用 controllerAs 选项来为controller指定一个别名。  你需要给指令定义一个用于使用的scope配置,在指令作为一个组件来使用的时候,这是特别有用的哦!

再来回顾一下myPane 的定义,注意 link函数的最后的一个参数: tabsCtrl。 当一个指令需要一个控制器的时候,在 link 函数中,会传到第四个参数中去。我们可以在第四次参数中得到。 利用这一点,myPane可以调用myTabs中的addPane功能了。

如果你需要多个控制器的话,可以给 require选项传入一个数组。如下:

angular.module('docsTabsExample', [])
.directive('myPane', function() {
return {
require: ['^myTabs', '^ngModel'],
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
link: function(scope, element, attrs, controllers) {
var tabsCtrl = controllers[0],
modelCtrl = controllers[1]; tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});

有些读者可能很想知道 link 函数和 controller之间的区别。 它们最基本的区别是,controller是一个暴露的API, 而link 函数是让元素和控制器进行交互的。

建议: 当你确定你需要向外,向其他指令暴露一个API的时候,你就得使用controller,其它的情况,还是使用 link比较好!!!

 

简单说明一下,这一章很重要,但是这里呢,我们只是写了一些指令的用例,但是每个用例都是你去创造更多指令的一个很不错的起点哦!!  发挥你的想要把!

 

如果你有兴趣了解一下编译过程的话,你可以点击这里

如果你想要查看一下 $compile API 编译器提供的详细的指令的话,你可以点击 这里

AngularJS -- 指令(创建自定义指令)的更多相关文章

  1. 带你走近AngularJS 之创建自定义指令

    带你走近AngularJS 之创建自定义指令 为什么使用AngularJS 指令? 使用过 AngularJS 的朋友应该最感兴趣的是它的指令.现今市场上的前端框架也只有AngularJS 拥有自定义 ...

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

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

  3. 在vue中创建自定义指令

    原文:https://dev.to/ratracegrad/creating-custom-directives-in-vue-58hh 翻译:心上有杨 指令是带有 v- 前缀的特殊属性.指令的作用是 ...

  4. Angular中的内置指令和自定义指令

    NG中的指令,到底是什么(what)? 为什么会有(why)?以及怎样使用(how)? What: 在NG中,指令扩展HTML功能,为 DOM 元素调用方法.定义行为绑定数据等. Why: 最大程度减 ...

  5. VUE:内置指令与自定义指令

    VUE:内置指令与自定义指令 常用的内置指令 1)v:text 更新元素的 textContent 2)v-html 更新元素的 innerHTML 3)v-if 如果为true,当前标签才会输出到页 ...

  6. 【AngularJS】—— 8 自定义指令

    AngularJS支持用户自定义标签属性,在不需要使用DOM节点操作的情况下,添加自定义的内容. 前面提到AngularJS的四大特性: 1 MVC 2 模块化 3 指令 4 双向数据绑定 下面将会介 ...

  7. angularJs工作日记-自定义指令Directive01

    新项目组使用完善的angularMVVM设计思路架构,很庆幸能够来到这个项目组,在这里的每一天都能够学习到新的知识,为了防止以后忘记,记录一下个人的理解 首先接触最多的是directive,direc ...

  8. 【vuejs深入一】深入学习vue指令,自定义指令解决开发痛点

    写在前面  一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 最近博主我沉淀了几个月,或者说懒了几个月.然而大佬的指点总是一针见血,能够让人看到方向.所以我现在有觉得,一个好的 ...

  9. 深入学习vue指令,自定义指令解决开发痛点

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code v-model指令 vue.js的定义是一个mvvm框架,将它发挥到极致能够极大的提升 ...

随机推荐

  1. C4.5算法(摘抄)

    1. C4.5算法简介 C4.5是一系列用在机器学习和数据挖掘的分类问题中的算法.它的目标是监督学习:给定一个数据集,其中的每一个元组都能用一组属性值来描述,每一个元组属于一个互斥的类别中的某一类.C ...

  2. GHO2VMDK转换工具分享含VS2010源码

    平常经常用到虚拟机,每次从gho转换为vmdk时都要输入cmd代码,觉得麻烦,自己动手做了个gho2vmdk转换工具,集成ghost32.exe文件,可以一键转换,省时省事.运行时会将ghost32. ...

  3. 如何编写更好的SQL查询:终极指南-第二部分

    上一篇文章中,我们学习了 SQL 查询是如何执行的以及在编写 SQL 查询语句时需要注意的地方. 下面,我进一步学习查询方法以及查询优化. 基于集合和程序的方法进行查询 反向模型中隐含的事实是,建立查 ...

  4. hdu 6068--Classic Quotation(kmp+DP)

    题目链接 Problem Description When online chatting, we can save what somebody said to form his ''Classic ...

  5. jQuery控制a标签不可点击 不跳转

    jquery禁用a标签方法1 01 02 03 04 05 06 07 08 09 10 11 12 $(document).ready(function () {         $("a ...

  6. WPF-TreeView获取文件夹目录、DataGrid获取目录下文件信息

    开发一个WPF桌面应用程序.刚接触WPF编程以及C#语言,这里把一些关键的问题记录下来. 下面是实现将路径的文件夹信息绑定到TreeView及DataGrid上显示. 关键问题是C#数据绑定方式及IE ...

  7. PowerBI入门 第三篇:报表设计技巧

    最近做了几个PowerBI报表,对PowerBI的设计有了更深的理解,对数据的塑形(sharp data),不仅可以在Data Source中实现,例如在TSQL查询脚本中,而且可以在PowerBI中 ...

  8. 转: 【Java并发编程】之十三:生产者—消费者模型(含代码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一 ...

  9. 201521123081《Java程序设计》 第8周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 参考资料:XMIND 1.2 选做:收集你认为有用的代码片段 2. 书面作业 本次作业题集 集合 Q1. Li ...

  10. Java第七周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 2. 书面作业 ArrayList代码分析 1.1 解释ArrayList的contains源代码 ...