angularjs directive学习心得

一些常见的错误

  1. 在angularjs里,创建directive时,directive的名称应该要使用驼峰式,例如myDirective,而在html里要调用它的时候,就不能用驼峰式了,可以用my-directive或者my_directive,一般都使用前者吧,统一下规范。
  2. 若声明了compile函数,link函数就不会被调用

transclude

transclude有三个选项,true, false, 和object.如果不显示指明的话,默认为false. 当为false的时候,则那个directive里面的指令不会嵌入到你写的模板里,举个例子 下面是html代码

<div my-directive>
   <h1>Hello</h1>
   <div>all the content will be erased</div>
</div>

下面是javascript代码

angular.module("app")
    .directive("myDirective", function() {
      return {
          restrict: "A",
          template: "<div>This is my template</div>"
        }
    })

运行结果如下图:

这时候会发现,原来你在div里面写的内容全部不见了,直接被template覆盖了,这就是使用transclude:false的情况,原来那个directive里面的内容都不会嵌入到你写的模板来。 当然,我们可以使用transclude:true来解决这个问题。 只改变原来的directive代码,如下:

.directive("myDirective", function() {
    return {
      restrict: "A",
      transclude: true,
      template: "<div>This is my template</div> <div ng-transclude></div>"
    }
})

运行结果如下图:

这个时候,原来那个div里面的所有内容都被放置到了ng-transclude声明的那个div里面了。因此只需要把transclude设置为true,然后在你的template里,在你想要原来指令放置在那里的地方,加一个ng-transclude,就可以将其放在里面.

那么,第三个选项object到底是干嘛的呢?还是让我们用一个例子来说明 将之前的html代码修改如下:

    <div my-directive>
        <transclude-name>CJG</transclude-name>
        <transclude-school>SYSU</transclude-school>
        <h1>路人甲</h1>
        <h2>路人乙</h2>
    </div>

以及javascript代码如下:

    .directive("myDirective", function() {
    return {
      restrict: "A",
      transclude: {
        "name": "?transcludeName",
        "school": "?transcludeSchool"
      },
      template: "<div>This is my template</div> <div ng-transclude></div>" +
                "<div ng-transclude='name'></div> <div ng-transclude='school'></div>";
    }
  })

在这里,我们在html里增加了一些标签,然后在transclude里,给一些标签设置了一些名字,然后我们就可以在template里,让ng-transclude="你设置的名字"来将你某些特定的内容放在特定的位置,当然,你如果直接使用ng-transclude的话,就默认将所有你没有设置名字的标签全部移到里面.(这里在标签名字前面增加一个?号,是为了防止标签不存在而报错) 运行结果如下:

到这里,transclude的几个属性值就已经介绍完了,然而transclude还有一个坑,就是你如果不做特殊处理的话,他会创建一个单独的作用域,与外界分隔开,这就会导致你无法访问到之前的变量,还是让我们来看一个例子. 这里,我们先写了一个controller,里面只有一个$scope.name变量

(function() {
  var app = angular.module("app", []);
  angular
    .module("app")
    .controller("myCtrl", function($scope) {
      $scope.name = 'CJG';
    })
})()

之后,我们在html里,将$scope.name显示出来

<div ng-controller="myCtrl" my-exam>
   <h1>my</h1>
   <h1>{{name}}</h1>
</div>

然后,我们像之前一样,写一个简单的directive,利用ng-transclude将原来div的内容放到我们指定的区域内

angular.module("app")
    .directive("myExam", function($compile) {
    return {
      restrict: "A",
      transclude: true,
      template: "<h1>Hello</h1> <div ng-transclude></div>"
    }
  })

那么,运行结果会不会和我们预期的一样,在ng-transclude里显示两个h1标签呢?让我们来看下

由上图可知,只显示了一个h1,而那个{{name}}没有显示出来,那么他有渲染吗?

由上图可以看到,他是有渲染两个div的,可是为什么就是没有值呢?原因就是因为,你使用transclude的话,默认是会创建一个新的作用域的,因此你就无法访问到之前作用域的值了。那么,怎么解决这个问题呢?

看了很多资料,我觉得比较有用的解决方法是以下两个:

1.使用transclude函数来将解决。transclude的函数原型为: transclude(scope, function(clone){}),我们可以将这个directive的scope传入给他,这样transclude就不会默认产生新的作用域,而是沿用我传给他的那个作用域,当然,你也可以根据自己的需求,传入你想传给他的scope,代码修改如下:

angular.module("app")
    .directive("myExam", function($compile) {
    return {
      restrict: "A",
      transclude: true,
      template: "<h1>Hello</h1>",
      compile: function(elem, attrs, transclude) {
        return function(scope, element, attrs) {
          transclude(scope, function(clone) {
            elem.append(clone);
          })
        }
      }
    }
  })

运行效果如下:

此时,就可以正常运转了。不过这个必须依赖于complie函数,然后通过他返回的link函数给transclude的内容一个作用域,然后将transclude的内容加载到页面里。 2.这种方法,其实讲道理根本不算用transclude的做法,不过也算是一种方法吧。。。。

angular.module("app")
  .directive("myExam", function($compile) {
    return {
      restrict: "A",
      link: function(scope, element, attrs) {
        var subScope = scope.$new(true);
        var newNode = $compile("<h1>Hello</h1>")(subScope);
        element.append(newNode);
      }
    }
   })

其实就是将你要加入的内容在link里面编译,然后用$scope.$new为它创建一个作用域,然后把它加到里面去。(我也不知道这算不算方法)

require

这个参数是用来加载其他directive的controller用的,比如你可能需要到父元素的controller的一些变量或者方法,那么你就可以通过他来访问父元素的controller ,示例如下:

angular.module("app")
    .directive("myExam", function($compile) {
    return {
      restrict: "A",
      require: "?^myParent",
      link: function(scope, element, attrs, ctrl) {
        console.log(ctrl);
        ctrl.sayHello();
        var subScope = scope.$new(true);
        var newNode = $compile("<h1>Hello</h1>")(subScope);
        element.append(newNode);
      }
    }
  })
  .directive("myParent", function() {
    return {
      restrict:"A",
      controller: function myParent($scope) {
        vm = this;
        vm.name ="CJG"
        vm.sayHello = function() {
          console.log("hello");
        }
      }
    }

html代码如下:

    <div my-parent>
      <div ng-controller="myCtrl" my_exam>
        <h1>my</h1>
        <h1>{{name}}</h1>
      </div>
    </div>

在这里,我们在my-exam的directive里引用了他的父亲my-parent的控制器,然后调用了它的方法,运行效果如下:

加?^的前缀是为了防止找不到控制器的时候,报错,让整个程序崩掉,具体的前缀情况如下:

    • (no prefix) - Locate the required controller on the current element. Throw an error if not found.
    • ? - Attempt to locate the required controller or pass null to the link fn if not found.
    • ^ - Locate the required controller by searching the element and its parents. Throw an error if not found.
    • ^^ - Locate the required controller by searching the element's parents. Throw an error if not found.
    • ?^ - Attempt to locate the required controller by searching the element and its parents or pass
  • null to the link fn if not found.
    • ?^^ - Attempt to locate the required controller by searching the element's parents, or pass
  • null to the link fn if not found.

restrict

restrict有四个选项,"A"(属性值),"E"(元素),"C"(类名),"M"(评论) 比如你将一个声明为E的话,那么你只能通过来调用它,不过一个directive可以声明为多个选项.

template

一个html段

templateUrl

类似于html段,不过就是将它单独写在一个文件里,通过url异步加载进来,compile在它的加载过程中,即使之前使用缓存,它也会去执行别的directive的编译工作,这样就不会导致阻塞。

compile

该函数有三个参数,tElement,tAttrs,transclude,该函数主要用于改变template DOM,而link函数则主要用于注册一些监听事件和执行directive的大多数逻辑. 这个时候就涉及到html的一个渲染过程了:

  1. 浏览器先加载所有的html标识,将其转化为DOM,当浏览器遇到angularjs的时候,就会停止解析过程,去执行angularjs
  2. angularjs在DOM中搜索ng-app执行,若搜索到,则初始化一些必要的组件(即$injector、$compile服务以及$rootScope),然后从该元素开始执行angular的编译
  3. angularjs查看整一棵树,如果发现有directive,则将directive以及它的compile函数一起加入到待编译组里,等全部搜索完毕后,在根据他们的优先级对他们进行依赖注入和编译
  4. 编译运行完后,就会执行它们的链接函数,注册一些监听事件

具体的详情可以去看https://my.oschina.net/brant/blog/419641,我觉得这篇博客说的很清楚.

该函数有5个参数,scope, iElement, iAttrs, controller, transcludeFn controller我们可以通过之前的require属性传过来。

attrs

directive可以利用attrs来做很多事情,比如,通过attr来访问共同的attribute对象,可以通过$observe来观察attribute值的变化

  .directive("myExam", function($compile) {
    return {
      replace: true,
      restrict: "A",
      require: "?^myParent",
      link: function(scope, element, attrs, ctrl) {
        // console.log(ctrl);
        ctrl.sayHello();
        scope.school = scope.$eval(attrs.ngModel);
        var newNode = $compile("<h1>Hello {{ name }} </h1> <h1>school {{ school}}</h1>")(scope);
        element.append(newNode);
        scope.$watch(
          function() {
            return scope.$eval(attrs.ngModel);
          },
          function() {
            scope.school = scope.$eval(attrs.ngModel)
          })
      }
    }
  })

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

Golang中defer 的五个坑-第三部分

译注:全文总共有四篇,本文为同系列文章的第三篇 本文将侧重于讲解使用 defer 的一些技巧 如果你对 defer 的基本操作还没有清晰的认识,请先阅读这篇文章...

23650
来自专栏烙馅饼喽的技术分享

用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- CustomYieldInstruction 自定义中断指令

ActionScript3脚本引擎为了方便热更新逻辑开发,提供的从脚本继承Unity类库功能在一些情况下可以提供开发的便利。 这次来建立一个示例,演示一下如何在...

36690
来自专栏IT笔记

Linux下安装Redis3

下载 下载地址 http://redis.io/download Linux 下执行 wget http://download.redis.io/relea...

38950
来自专栏高爽的专栏

Java线程(四):线程中断、线程让步、线程睡眠、线程合并

最近在Review线程专栏,修改了诸多之前描述不够严谨的地方,凡是带有Review标记的文章都是修改过了。本篇文章是插进来的,因为原来没有写,现在...

28100
来自专栏用户画像

指令及操作数的寻址方式

  操作数的寻址:把操作数的形式地址,根据间址和变址等组合变换为操作数有效地址的过程。

48920
来自专栏大内老A

.NET Core采用的全新配置系统[3]: “Options模式”下的配置是如何绑定为Options对象

配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置。值得推荐的做法就是采用《.NET Cor...

207100
来自专栏Youngxj

Goto循环-c语言学习笔记

15530
来自专栏奔跑的蛙牛技术博客

synchronized 详解

那到底使用synchronized关键字是不是就是一个监视器? 不懂,理解的话可以向我发邮件gaomengjie_1@163.com,我虚心求教

8430
来自专栏好好学java的技术栈

java基础提升篇:深入浅出Java多线程

13920
来自专栏扎心了老铁

python使用上下文管理器实现sqlite3事务机制

如题,本文记录如何使用python上下文管理器的方式管理sqlite3的句柄创建和释放以及事务机制。 1、python上下文管理(with) python上下文...

515120

扫码关注云+社区

领取腾讯云代金券