AngularJS 讲解,四 Directive

AngularJS  Directive 自定义指令(我最喜欢AngularJs的功能之一)

一:什么时候我们会用到directive

    1.使html更具语义化,不用深入了解研究代码的逻辑便可知道大致逻辑。

  2.抽象出一个自定义组件,可以重复使用。

二:directive的定义及其使用方法

    1.下面是一个directive参数详细模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
angular.module('app',[]);//申明一个调用angularjs块
   
angular.module('app').directive('directiveName', function factory() {
   
 var directiveDefinitionObject = {
   priority: 0,<br>   restrict:'A',
   template: '<div></div>',
   templateUrl: 'directive.html',
   replace: false,
   transclude: false,
   scope: false,
   compile: function compile(tElement, tAttrs, transclude) {
     return {
       pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       post: function postLink(scope, iElement, iAttrs, controller) { ... }
    }
  },
   link: function postLink(scope, iElement, iAttrs) { ... }
};
 return directiveDefinitionObject;
});

  2.参数详解

       priority:

    (数字),可选参数,指明指令的优先级,若在单个DOM上有多个指令,则优先级高的先执行;

   terminal:

    (布尔型),可选参数,值为true或false,若设置为true,则优先级低于此指令的其他指令则无效,不会被调用(优先级相同的还是会执行);

      restrict:

          (字符串)可选参数,指明指令在DOM里面以什么形式被声明;

    取值有:E(元素),A(属性),C(类),M(注释),其中默认值为A;当然也可以两个一起用,比如EA.表示即可以是元素也可以是属性。

    E(元素):<directiveName></directiveName>  
    A(属性):<div directiveName='expression'></div>  
    C(类): <div class='directiveName'></div>  
    M(注释):<--directive:directiveName expression-->  
    一般情况下E/A/C用得比较多。

  template:

    (字符串或函数),可选参数

    例:(1)字符串时:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="zh" ng-app="app">
<head>
 <meta charset="UTF-8">
 <title>AngularJS Directive</title>
 <script type="text/javascript" src="angular.min.js"></script>
</head>
<body>
<hello-world></hello-world>
</body>
<script type="text/javascript">
angular.module('app', []);
angular.module.('app').directive('helloWorld', function() {
 return {
 restrict: 'E',
 template: '<div><h1>Hi I am hello world.</h1></div>',
 replace: true
 };
});
</script>
</html>

   (2)函数时:有两个参数tElement和tAttrs

      tElement:使用此指令的元素

        tAttrs:该指令元素上的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="zh" ng-app="app">
<head>
 <meta charset="UTF-8">
 <title>AngularJS Directive</title>
 <script type="text/javascript" src="angular.min.js"></script>
</head>
<body>
<hello-world></hello-world>
<hello-world2 title = 'I am a Hello World 2.'></hello-world2>
</body>
<script type="text/javascript">
angular.module('app', []);
app.directive('helloWorld', function() {
 return {
 restrict: 'E',
 template: '<div><h1>Hi I am a Hello Worl.</h1></div>',
 replace: true
 };
});
app.directive("helloWorld2",function(){
 return{
 restrict:'EAC',
 template: function(tElement,tAttrs){
 var _html = '';
 _html += '<div>'+tAttrs.title+'</div>';
 return _html;
 }
 };
 });
</script>
</html>

  templateUrl:

    (字符串或者函数)可选参数

    字符串:代表HTML文件路径的字符串

    函数:可接收两个参数tElement和tAttrs(大致同上)

    (PS:由于加载html模板是通过异步加载的,若加载大量的模板会拖慢网站的速度,所以我们可以先缓存模板)

    例:(把要加载的页面 先加载好 包含在你需要的html里)    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="zh" ng-app="app">
<head>
 <meta charset="UTF-8">
 <title>AngularJS Directive</title>
 <script type="text/javascript" src="angular.min.js"></script>
</head>
<body>
<hello-world></hello-world>
</body>
<script type="text/javascript">
angular.module('app', []);
angular.module("app").directive('helloWorld', function() {
 return {
 restrict: 'E',
 templateUrl: 'hello.html',
 replace: true
 };
});
</script>
<script type='text/ng-template' id='hello.html'>
 <div><h1>Hi I am Hello world.</h1></div>
</script>
</html>

 还有一种方法:

1
2
3
4
angular.module("app").run(["$templateCache", function($templateCache) {
 $templateCache.put("hello.html",
 "<div><h1>Hi I am Hello world.</h1></div>");
}]);

  replace:

    (布尔值):默认值是false. true:替换掉指令即(<hello-world></hello-world>),false:不替换

  transclude:(是否想要指令内部的内容被模板替换掉)

    (布尔值):默认值是false. true:需要和ng transclude一起使用

    例:template:"<div>hello every <div ng-transclude></div></div>"

        这时,指令内部的内容会嵌入到ng-transclude这个div中。也就是变成了<div>hello every <div>这是指令内部的内容</div></div>。

  scope:

    1)默认值false。表示继承父作用域;(继承不隔离)

    2)true。表示继承父作用域,并创建自己的作用域(子作用域);(继承隔离)

    3){}。表示创建一个全新的隔离作用域;(不继承隔离)

    (PS:当你想要创建一个可重用的组件时隔离作用域是一个很好的选择,通过隔离作用域我们确保指令是‘独立'的,并可以轻松地插入到任何HTML app中,并且这种做法防止了父作用域被污染;)

    下面针对隔离作用域进行详细讲解:

       a.隔离作用域怎样去访问父作用域:

       Directive 在使用隔离 scope 的时候,提供了三种方法同隔离之外的地方交互.

      1). @ 

        (用来访问 directive 外部环境定义的字符串值,主要是通过 directive 所在的标签属性绑定外部字符串值。这种绑定是单向的,即父 scope 的绑定变化,directive 中的 scope 的属性会同步变化,                                     而隔离 scope 中的绑定变化,父 scope 是不知道的。)PS:相当于继承隔离

        scope:{

        name:"@"

        }     

      2). &

        (& 方式提供一种途经是 directive 能在父 scope 的上下文中执行一个表达式。当 directive 中有什么动作需要更新到父 scope 中的时候,可以在父 scope 上下文中执行一段代码或者一个函数。PS:即绑定的是方法)

        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="zh" ng-app="app">
<head>
 <meta charset="UTF-8">
 <title>AngularJS Directive</title>
 <script type="text/javascript" src="angular.min.js"></script>
</head>
<body>
 <div ng-controller="myController">
 <div>父scope:
 <div>Say:{{value}}</div>
 </div>
 <div>隔离scope:
 <div isolated-directive action="click()"></div>
 </div>
</div>
</body>
<script type="text/javascript">
angular.module('app', []);
app.controller("myController", function ($scope) {
 $scope.value = "hello world";
 $scope.click = function () {
 $scope.value = Math.random();
 };
 }).directive("isolatedDirective", function () {
 return {
 scope: {
 action: "&"
 },
 template: '<input type="button" value="在directive中执行父scope定义的方法" ng-click="action()"/>'
 }
 })
</script>
</html>

 

      3). =

       (通过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间建立双向绑定。
        意思是,当你想要一个双向绑定的属性的时候,你可以使用=来引入外部属性。无论是改变父 scope 还是隔离 scope 里的属性,父 scope 和隔离 scope 都会同时更新属性值,因为它们是双向绑定的关系。)

      例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="zh" ng-app="app">
<head>
 <meta charset="UTF-8">
 <title>AngularJS Directive</title>
 <script type="text/javascript" src="angular.min.js"></script>
</head>
<body>
<div ng-controller="myController">
 <div>父scope:
 <div>Say:{{user.name}}<br>改变父scope的name:<input type="text" value="" ng-model="userBase.name"/></div>
 </div>
 <div>隔离scope:
 <div isolated-directive user="userBase"></div>
 </div>
</div>
</body>
<script type="text/javascript">
angular.module('app', []);
angular.module.("app").controller("myController", function ($scope) {
 $scope.userBase = {
 name: 'hello',
 id: 1
 };
 }).directive("isolatedDirective", function () {
 return {
 scope: {
 user: "="
 },
 template: 'Say:{{user.name}} <br>改变隔离scope的name:<input type="buttom" value="" ng-model="user.name"/>'
 }
 })
</script>
</html>

   controller:

    (可以是一个字符串或者函数)

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
angular.module('app', [])
angular.module.("app").directive('myDirective', function() {
restrict: 'A',
controller: 'DefinitionController'
})
// 应用中其他的地方,可以是同一个文件或被index.html包含的另一个文件
angular.module('app')
.controller('DefinitionController', function($scope, $element, $a
也可以直接在指令内部的定义为匿名函数,同样我们可以再这里注入任何服务($log,$timeout等等)
  
js:
angular.module('app',[])
.directive('myDirective', function() {
restrict: 'A',
controller:
function($scope, $element, $attrs, $transclude) {
// 控制器逻辑放在这里
}
});

另外还有一些特殊的服务(参数)可以注入

(1)$scope,与指令元素相关联的作用域

(2)$element,当前指令对应的 元素

(3)$attrs,由当前元素的属性组成的对象

(4)$transclude,嵌入链接函数,实际被执行用来克隆元素和操作DOM的函数

注意: 除非是用来定义一些可复用的行为,一般不推荐在这使用。
         指令的控制器和link函数(后面会讲)可以进行互换。区别在于,控制器主要是用来提供可在指令间复用的行为但link链接函数只能在当前内部指令中定义行为,且无法再指令间复用。

   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="zh" ng-app="app">
<head>
 <meta charset="UTF-8">
 <title>AngularJS入门学习</title>
 <script type="text/javascript" src="angular.min.js"></script>
</head>
 <hello mycolor ="red">I am Hello World.</hello>
</body>
<script type="text/javascript">
angular.module('app', []);
angular.module("app").directive('hello', function() {
 return {
  restrict: 'EA',
  transclude: true, //注意此处必须设置为true
  controller:
  function ($scope, $element,$attrs,$transclude,$log) { //在这里你可以注入你想注入的服务
  $transclude(function (clone) {
   var a = angular.element('<p>');
   a.css('color', $attrs.mycolor);
   a.text(clone.text());
   $element.append(a);
  });
  $log.info("hello everyone");
  }
 };
 });
</script>
</html>

   这里如果我们想要实用的父作用域:$scope.$parent

                               新的作用域:$scope.$parent.new()

  controllerAs 

    设置控制器的别名(angular1.2给我们带来的新的语法糖)

           例:

1
2
3
4
5
6
7
8
9
10
11
<script>
 angular.module('app',[]).directive('myDirective', function () {
 return {
  restrict: 'EA',
  transclude: true,
  controller:'someController',
  controllerAs:'mainController'
  //..其他配置
 };
 });
 </script>

   require

    字符串或数组

      字符串代表另一个指令的名字,作为link函数的第四个参数。假设现在我们要编写两个指令,两个指令中的link链接函数中存在有很多重合的方法,这时候我们就可以将这些重复的方法写在第三个指令的controller中(上面也讲到controller经常用来提供指令间的复用行为)然后在这两个指令中,require这个拥有controller字段的的指令(第三个指令),最后通过link链接函数的第四个参数就可以引用这些重合的方法了。

    例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!doctype html>
<html ng-app="app">
<head>
 <script src="angular.min.js"></script>
</head>
<body>  
 <outer-directive>
 <inner-directive></inner-directive>
 <inner-directive2></inner-directive2>
 </outer-directive>
 <script>
 angular.module('app', []);
 angular.module("app").directive('outerDirective', function() {
  return {
  scope: {},
  restrict: 'AE',
  controller: function($scope) {
   this.say = function(someDirective) {
   console.log('Show:' + someDirective.message);
   };
  }
  };
 });
 angular.module("app").directive('innerDirective', function() {
  return {
  scope: {},
  restrict: 'AE',
  require: '^outerDirective',
  link: function(scope, elem, attrs, controllerInstance) {
   scope.message = "Hi,I am Ruby.";
   controllerInstance.say(scope);
  }
  };
 });
 app.directive('innerDirective2', function() {
  return {
  scope: {},
  restrict: 'AE',
  require: '^outerDirective',
  link: function(scope, elem, attrs, controllerInstance) {
   scope.message = "Hi,I am Hello World.";
   controllerInstance.say(scope);
  }
  };
 });
   
   
 </script>
   
</body>
</html>

     require参数有四种:   

      1)没有前缀,指令会在自身提供的控制器中进行查找,如果找不到任何控制器,则会抛出一个error

      2)?如果在当前的指令没有找到所需的控制器,则会将null传给link连接函数的第四个参数

      3)^如果在当前的指令没有找到所需的控制器,则会查找父元素的控制器

      4)?^组合

  compile function

    function compile(tElement, tAttrs, transclude) { ... }

    编译函数用来修改模板dom的(不常用)

    需要用到编译函数例如:ngTrepeat;ngView(需要异步载入内容的)。

    各参数意义:

    tElement - template element 该指令所在的元素。

      tAttrs - template attributes 指令元素上所声明的属性。

    transclude - 一个嵌入的链接函数function(scope, cloneLinkingFn)。

    注意:在编译函数里面不要进行任何DOM变形之外的操作。 更重要的,DOM监听事件的注册应该在链接函数中做,而不是编译函数中。 编译函数可以返回一个对象或者函数。 返回函数 - 等效于在编译函数不存在时,使用配置对象的link属性注册的链接函数。 返回对象 - 返回一个通过pre或post属性注册了函数的对象。参考下面pre-linking和post-liking函数的解释。

  link function

    function link(scope, iElement, iAttrs, controller) { ... }
    链接函数负责注册DOM事件和更新DOM。它是在模板被克隆之后执行的,它也是大部分指令逻辑代码编写的地方。
    scope - 指令需要监听的作用域。
    iElement - instance element - 指令所在的元素。只有在postLink函数中对元素的子元素进行操作才是安全的,因为那时它们才已经全部链接好。
    iAttrs - instance attributes - 实例属性,一个标准化的、所有声明在当前元素上的属性列表,这些属性在所有链接函数间是共享的。
    controller - 控制器实例,也就是当前指令通过require请求的指令someDirective内部的controller。比如:someDirective指令中的controller:function(){this.Say = function(){}},那么,在当前指令的link函数中,你就可以通过controller.Say进行调用了。
    Pre-linking function 在子元素被链接前执行。不能用来进行DOM的变形,以防链接函数找不到正确的元素来链接。
    Post-linking function 所有元素都被链接后执行。

  compile function 和 link function需要注意的地方:两者是互斥的,compile function负责对模板dom进行修改,link function负责将作用域和dom进行连接。

                          当两者共存时,compile function返回的函数当作link function,link function则被忽略。

  

    

 

  

  

 

 

 

  

    

posted @   菠萝蜜小兔  阅读(426)  评论(0编辑  收藏  举报
编辑推荐:
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
阅读排行:
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络
点击右上角即可分享
微信分享提示