AngularJS自定义控件实例详解

本文实例讲述了AngularJS自定义控件。分享给大家供大家参考,具体如下:

自定义指令介绍

AngularJS 指令作用是在 AngulaJS 应用中操作 Html 渲染。比如说,内插指令 ( {{ }} ), ng-repeat 指令以及 ng-if 指令。

当然你也可以实现自己的。这就是 AngularJS 所谓的"教会 HTML 玩新姿势”。本文将告诉你如何做到。

指令类型

可以自定义的指令类型如下:

元素
属性
CSS class
Comment directives

这里面,AngularJS 强烈建议你用元素和属性类型,而不用 CSS class 和 comment directives (除非迫不得已)。

指令类型决定了指令何时被激活。当 AngularJS 在 HTML 模板中找到一个 HTML 元素的时候,元素指令被激活。当 AngularJS 找到一个 HTML 元素属性时,属性指令被激活。当 AngularJS 找到一个 CSS class 时, CSS class 指令被激活,最后,当 AngularJS 找到 HTML comment 时,comment directive 被激活。

基础例子

你可以向模块注册一个指令,像这样:

<!-- lang: js -->
myapp = angular.module("myapp", []);
myapp.directive('div', function() {
  var directive = {};
  directive.restrict = 'E'; /* restrict this directive to elements */
  directive.template = "My first directive: {{textToInsert}}";
  return directive;
});

来看模块中调用的 directive() 函数。当你调用该函数时,意味着你要注册一个新指令。directive() 函数的第一个参数是新注册指令的名称。这是之后你在 HTML 模板中调用它的时候用的名称。例子中,我用了 'div' ,意思是说当 HTML 模板每次在找到 HTML 元素类型是 div 的时候,这个指令都会被激活。

传递给 directive 函数的第二个参数是一个工厂函数。调用该函数会返回一个指令的定义。 AngularJS 通过调用该函数来获取包含指令定义的 JavaScript 对象。如果你有仔细看上面的例子,你会知道它返回的确是是一个 Javascript 对象。

这个从工厂函数中返回的 Javascript 对象有两个属性: restrict 和 template 字段。

restrict 字段用来设置指令何时被激活,是匹配到 HTML 元素时,还是匹配到元素属性时。也就是指令类型。把restrict 设置为 E 时,只有名为 div 的 HTML 元素才会激活该指令。把 restrict 设置为 A 时,只有名为 div 的 HTML 元素属性才会激活该指令。你也可以用 AE ,这样会使得匹配到元素名和元素属性名时,都可以激活该指令。

template 字段是一个 HTML 模板,用来替换匹配的 div 元素。它会把匹配到的 div 当成一个占位符,然后用 HTML 模板在同一位置来替换掉它。也就是说,HTML 模板替换匹配的到 HTML 元素。

比如说你的 HTML 页面有这样一段 HTML:

<!-- lang: js -->
<div ng-controller="MyController" >
  <div>This div will be replaced</div>
</div>

当 AngularJS 找到内嵌的 div 的时候,会激活自定义的指令。然后把该 div 元素替换掉,替换后的 HTML 将变成这样:

<!-- lang: js -->
My first directive: {{textToInsert}}

然后你看到了,这段 HTML 里面包含了一个内插指令 ({{textToInsert}})。AngularJS 会再执行一次,让内插指令实际值显示出来。然后 $scope.textToInsert 属性将会在这个 HTML 点上替换掉内插指令占位符。

template 和 templateUrl 属性

创建自定义指令最简单的例子就是上面这样了。你的指令用来生成 HTML,然后你把 HTML 放到指令定义对象的 template 属性中。下面这个例子是上面那最简单的例子的重复,注意 template部分:

<!-- lang: js -->
myapp = angular.module("myapp", []);
myapp.directive('div', function() {
  var directive = {};
  directive.restrict = 'E'; /* restrict this directive to elements */
  directive.template = "My first directive: {{textToInsert}}";
  return directive;
});

如果 HTML 模板越来越大,把它写在一个 Javascript 字符串中肯定非常难维护。你可以把它放到一个单独的文件中,然后 AngularJS 可以通过这个文件把它加载进来。然后再把 HTML 模板文件的 URL 放到指令定义对象的 templateUrl 属性中。下面是例子:

<!-- lang: js -->
myapp = angular.module("myapp", []);
myapp.directive('div', function() {
  var directive = {};
  directive.restrict = 'E'; /* restrict this directive to elements */
  directive.templateUrl = "/myapp/html-templates/div-template.html";
  return directive;
});

然后 AngularJS 会从 templateUrl 属性中设置的 URL 将 HTML 模板加载进来。

用独立的 HTML 模板文件,设置 templateUrl 属性,在你要创建更多的通用指令时会显得更加有用,比如说用来显示用户信息的指令。例子:

<!-- lang: js -->
myapp = angular.module("myapp", []);
myapp.directive('userinfo', function() {
  var directive = {};
  directive.restrict = 'E'; /* restrict this directive to elements */
  directive.templateUrl = "/myapp/html-templates/userinfo-template.html";
  return directive;
});

例子创建了一个指令,当AngularJS 找到一个 <userinfo> 元素的时候就会激活它。AngularJS 加载指向 /myapp/html-templates/userinfo-template.html 的模板并解析它,它就像从一开始就被嵌在上一级 HTML 文件中一样。

从指令中隔离 $scope

在上面的例子中,把 userinfo 指令硬绑定到了 $scope ,因为 HTML 模板是直接引用 textToInsert 属性的。直接引用 $scope 让指令在同一个 controller 中的时候,非常难复用,因为 $scope在同一个 controller 中的值都是一样的。比如说,你在页面的 HTML 中这样写的时候:

<!-- lang: js -->
<userinfo></userinfo>
<userinfo></userinfo>

这两个 <userinfo> 元素会被同样的 HTML 模板取代,然后绑定到同样的 $scope 变量上。结果就是两个 <userinfo> 元素将会被一模一样的 HTML 代码给替换了。

为了把两个 <userinfo> 元素绑定到 $scope 中的不同的值,你需要给 HTML 模板一个 isolate scope。

所谓 isolate scope 是绑定在指令上的独立的 scope 对象。下面是定义的例子:

<!-- lang: js -->
myapp.directive('userinfo', function() {
  var directive = {};
  directive.restrict = 'E';
  directive.template = "User : {{user.firstName}} {{user.lastName}}";
  directive.scope = {
    user : "=user"
  }
  return directive;
})

请看 HTMl 模板中的两个内插指令 {{user.firstName}} 和 {{user.lastName}}。注意 user. 部分。以及directive.scope 属性。 directive.scope 是个带 user 属性的 Javascript 对象。于是directive.scope 就成为了 isolate scope 对象,现在 HTML 模板被绑到了 directive.scope.user 上(通过 {{user.firstName}} 和 {{user.lastName}} 内插指令)。

directive.scope.user 被设置为 “=user” 。意思是说 directive.scope.user 和 scope 中的属性绑在一起的(不是 isolate scope),scope 中的属性通过 user 属性传入 <userinfo> 元素。听起来挺费劲的,直接看例子:

<!-- lang: js -->
<userinfo user="jakob"></userinfo>
<userinfo user="john"></userinfo>

这两个 <userinfo> 元素都有 user 属性。user 的值指向了 $scope 中同名属性,被指定的 $scope中的属性将在 userinfo 的 isolate scope object 中被使用。

下面是完整的例子:

<!-- lang: js -->
<userinfo user="jakob"></userinfo>
<userinfo user="john"></userinfo>
<script>
myapp.directive('userinfo', function() {
  var directive = {};
  directive.restrict = 'E';
  directive.template = "User : <b>{{user.firstName}}</b> <b>{{user.lastName}}</b>";
  directive.scope = {
    user : "=user"
  }
  return directive;
});
myapp.controller("MyController", function($scope, $http) {
  $scope.jakob = {};
  $scope.jakob.firstName = "Jakob";
  $scope.jakob.lastName = "Jenkov";
  $scope.john = {};
  $scope.john.firstName = "John";
  $scope.john.lastName = "Doe";
});
</script>

compile() 和 link() 函数

如果你需要在你的指令中做更高级的操作,单纯使用 HTML 模板做不到的时候,你可以考虑使用compile() 和 link() 函数。

compile() 和 link() 函数定义了指令如何修改匹配到的 HTML。

当指令第一次被 AngularJS 编译的时候 (在 HTML 中第一次被发现),compile() 函数被调用。compile() 函数将为该元素做一次性配置。

然后 compile() 函数以返回 link() 作为终结。link() 函数将在每次元素被绑到 $scope 数据时,都被调用。

下面是例子:

<!-- lang: js -->
<script>
myapp = angular.module("myapp", []);
myapp.directive('userinfo', function() {
  var directive = {};
  directive.restrict = 'E'; /* restrict this directive to elements */
  directive.compile = function(element, attributes) {
    // do one-time configuration of element.
    var linkFunction = function($scope, element, atttributes) {
      // bind element to data in $scope
    }
    return linkFunction;
  }
  return directive;
});
</script>

compile() 函数带两个参数: element 和 attributes。

element 参数是 jqLite 包装过的 DOM 元素。AngularJS 有一个简化版 jQuery 可以用于操作 DOM,所以对 element 的 DOM 操作方式和你在 jQuery 中所知的一样。

attributes 参数是包含了 DOM 元素中的所有的属性集合的 Javascript 对象。因此,你要访问某个属性你可以这样写 attributes.type。

link() 函数带三个参数: $scope,element 和 attributes。element 和attributes 参数和传到 compile() 函数中的一样。$scope 参数是正常的 scope 对象,或者当你在指令定义对象中定义了 isolate scope 的时候,它就是 isolate scope。

compile() 和 link() 的命名相当混乱。它们肯定是受编译队伍的启发而命名的。我可以看到相似点,不过一个编译器是传入一次,然后输出。而指令则是配置一次 HTML 元素,然后在之后的 $scope 对象改变时进行更新。

compile() 函数如果叫做 create(), init() 或者 configure()也许会更好。这样可以表达出这个函数只会被调用一次的意思。

而 link() 函数如果叫 bind() 或者 render() 也许会更好,能更好的表达出这样的意思,在指令绑定数据或者重绑定数据的时候,这个函数将会被调用。

下面是一个完整的例子,演示了指令使用 compile() 和 link() 函数的:

<!-- lang: js -->
<div ng-controller="MyController" >
  <userinfo >This will be replaced</userinfo>
</div>
<script>
  myapp = angular.module("myapp", []);
  myapp.directive('userinfo', function() {
    var directive = {};
    directive.restrict = 'E'; /* restrict this directive to elements */
    directive.compile = function(element, attributes) {
      element.css("border", "1px solid #cccccc");
      var linkFunction = function($scope, element, attributes) {
        element.html("This is the new content: " + $scope.firstName);
        element.css("background-color", "#ffff00");
      }
      return linkFunction;
    }
    return directive;
  })
  myapp.controller("MyController", function($scope, $http) {
    $scope.cssClass = "notificationDiv";
    $scope.firstName = "Jakob";
    $scope.doClick = function() {
      console.log("doClick() called");
    }
  });
</script>

compile() 函数设置 HTML 元素的 border 。它只执行一次,因为 compile() 函数只执行一次。

link() 替换 HTML 元素内容,并且把背景颜色设置为黄色。

把设置 border 放在 compile(), 把背景颜色放在 link() 没啥特别的理由。你可以把所有的操作都丢到 compile(),或者都放到 link()。如果都放到 compile() 它们只会被设置一次(你需要它们是常量的话)。如果放到了 link(),它们会在每次 HTML 元素被绑到 $scope 的时候都发生变化。当你希望根据 $scope 中的数据来设置 boarder 和背景颜色的时候这非常有用。

只设置 link() 函数

有时候你的指令可能不需要 compile() 。你只需要用到 link()。这种情况下,你可以直接设置指令定义对象中的 link() 函数。下面是一个对上面例子的修改,只用 link 函数:

<!-- lang: js -->
<div ng-controller="MyController" >
  <userinfo >This will be replaced</userinfo>
</div>
<script>
  myapp = angular.module("myapp", []);
  myapp.directive('userinfo', function() {
    var directive = {};
    directive.restrict = 'E'; /* restrict this directive to elements */
    directive.link = function($scope, element, attributes) {
        element.html("This is the new content: " + $scope.firstName);
        element.css("background-color", "#ffff00");
    }
    return directive;
  })
  myapp.controller("MyController", function($scope, $http) {
    $scope.cssClass = "notificationDiv";
    $scope.firstName = "Jakob";
    $scope.doClick = function() {
      console.log("doClick() called");
    }
  });
</script>

注意 link() 方法和之前例子中返回的 link() 是完全一样的。

通过 Transclusion 封装元素的指令

到目前为止,我们看到的所有例子,都是把匹配到的元素内容给替换为指令的指定内容,不管是通过 Javascript 也好或者 HTML 模板也好。不过如果遇到内容中有部分是开发者可以指定的内容的时候呢?比如说:

<!-- lang: js -->
<mytransclude>This is a transcluded directive {{firstName}}</mytransclude>

标记为 <mytransclude> 的元素,它的部分内容可以由开发者设置。因此,这部分 HTML 不应该被指令的 HTML 模板给替换。我们实际上是希望这部分 HTML 由 AngularJS 来处理的。这个处理叫做 “transclusion”。 1

为了能让 AngularJS 把这部分 HTML 放到指令内部处理,你必须设置指令定义对象的 transclude 属性为 true。你还需要告诉 AngularJS,指令的 HTML 模板中哪一部分需要把 transcluded HTML 包含进来。你可以通过插入 ng-transclude 属性 (实际上,是一个指令) 到 HTML 模板的 HTML 元素中,标记你想要添加 transcluded HTML 的元素。

下面是一个 AngularJS 指令,演示如何使用 transclusion:

<!-- lang: js -->
<mytransclude>This is a transcluded directive {{firstName}}</mytransclude>
<script>
  myapp = angular.module("myapp", []);
  myapp.directive('mytransclude', function() {
    var directive = {};
    directive.restrict = 'E'; /* restrict this directive to elements */
    directive.transclude = true;
    directive.template = "<div class='myTransclude' ng-transclude></div>";
    return directive;
  });
  myapp.controller("MyController", function($scope, $http) {
    $scope.firstName = "Jakob";
  });
</script>

注意 <mytransclude> 元素内的 HTML。这部分 HTML 代码包含了内插指令 {{firstName}}。我们希望 AngularJS 来为我们处理这部分 HTML,让内插指令执行。为了实现这个目的,我在指令定义对象中把 transclude 设置为 true。我还在 HTML 模板中用到了 ng-transclude 属性。这个属性告诉 AngularJS 什么元素需要插入到 transcluded HTML。

1: 说实话,我没看懂那个定义,说的太TM难懂了,而且我好不爽写代码没输出的教程。只好自己动手做做例子。我觉得其实应该是这样的,把目标元素内容作为一个整体,拿到 HTML 模板中来,添加到 ng-transclude 指定的标签下。这个处理,我觉得应该就是叫做 transcluded。比如说刚才的例子(有些 bug,自己修正一下),因为 directive.transclude = true; ,所以原来 <mytransclude> 元素内的 HTML:

<!-- lang: js -->
This is a transcluded directive {{firstName}}

在激活指令 'mytransclude' 的时候,会被拿到 'mytransclude' 指令的模板中来,放到被 ng-transclude 指定的

<!-- lang: js -->
"<div class='myTransclude' ng-transclude></div>"

中。于是最终输出的结果应该是:

<!-- lang: js -->
<mytransclude>
  <div class='myTransclude' ng-transclude>
    <span class="ng-scope ng-binding">This is a transcluded directive Jakob</span>
  </div>
</mytransclude>

更多关于AngularJS相关内容感兴趣的读者可查看本站专题:《AngularJS入门与进阶教程》及《AngularJS MVC架构总结》

希望本文所述对大家AngularJS程序设计有所帮助。

(0)

相关推荐

  • 教你用AngularJS框架一行JS代码实现控件验证效果

    如上图所示,我们需要实现如下这些验证功能: 控件都是必输控件 都需要控制最大长度 第一次打开页面,控件不能显示为错误状态 输入内容再清空后,必输控件需要显示为错误状态 只有所有输入合法后,发布按钮才能变为可用状态 通过AngularJS,我们可以很轻松的实现这些要求,只需要1行JS代码.UI样式这里采用的是Bootstrap.先上示例代码: HTML. <!DOCTYPE html> <html lang="zh-cn" ng-app="ftitApp&qu

  • angularjs实现的前端分页控件示例

    前言:之前写个一个jQuery的分页显示插件,存在许多的bug,现在由于业务需要,学习的一点AngularJS,重新用angularjs实现了这个分页插件 实现效果图: (效果图是加上了bootstrap的css文件) 用法: angular-pagination.js代码: /** * angularjs分页控件 * Created by CHEN on 2016/11/1. */ angular.module('myModule', []).directive('myPagination',

  • 学习使用AngularJS文件上传控件

    前段时间做项目遇到一个需求是上传文件,大概需要实现的样式是这样子的,见下图: 需要同时上传两个文件.并且规定文件格式和文件大小.因为前端框架使用angular,且不想因为一个上传功能又引入一个jquery,所以在网上查找基于angular的上传控件,因为angular还算比较新,貌似都没有太成熟的插件,网上的教程也大多是复制粘贴,总之没起倒多大的作用...但是皇天不负有心人,最后还是让我遇到了这个功能强大的插件,让我有种相见恨晚的感觉呀,依靠官方文档和师兄的帮助,终于搞清楚了基本的使用方法.好东

  • AngularJS 文件上传控件 ng-file-upload详解

    网上可以找到的 AngularJS 的文件上传控件有两个: angular-file-upload:https://github.com/nervgh/angular-file-upload ng-file-upload:https://github.com/danialfarid/ng-file-upload 这两个非常类似,连js文件的结构都是一样的.核心的js是.min.js,还都有一个-shim.min.js,用来支持上传进度条和上传暂停等高级功能. 按道理讲shim.js应该是可加可不

  • ANGULARJS中使用JQUERY分页控件

    首篇,不知写何物,思来想去,敬上分页控件使用方法,望共同探讨. 分页乃前端数据展现之常用功能,而在我们使用的Angular js中,原生的分页需要将数据全部取到前端后,然后再到前端分页,在大批量数据操作时并不实用.接下来,我来介绍了将一种jquery的分页控件修改为Angularjs指令的方法. 首先在web项目中引用jquery1.10.Angularjs库文件以及jq-pagination控件. 我降指令名称为custompagination,为指令添加Html样式. 然后给指令添加对应的控

  • AngularJS实现根据不同条件显示不同控件

    由于项目需求,需要实现根据不同条件显示不同控件的功能.具体要求如下图所示: 即当选择"每单固定减"时,下方只显示"减免金额"一栏: 当选择"每单固定折扣"时,下方只显示"折扣比例"一栏: 当选择"每单满额减"时,下方只显示"满..减.."两栏. 根据自己对angular的了解,应该可以很轻松的实现此功能. js设置控件的隐藏与显示,设置控件style的display和visibility

  • AngularJS仿苹果滑屏删除控件

    AngularJs被用来开发单页面应用程序(SPA),利用AJAX调用配合页面的局部刷新,可以减少页面跳转,从而获得更好的用户体验.Angular的ngView及其对应的强大路由机制,是实现SPA应用的核心模块.本文所说的页面切换指的就是这个路由机制,即根据不同的url展示不同的视图. 前端开发中,为了对列表项进行快捷操作,有时就添个按钮来简单实现.但是,有时会发现按钮影响美观,甚至影响列表行的布局.稍在网上搜索无果,而写此仿苹果滑屏删除控件. 依赖项:angularJS.jQuery 测试浏览

  • Angularjs中使用layDate日期控件示例

    layDate 控件地址:http://laydate.layui.com/ 前情:原来系统中使用的日期控件是UI bootstrap(地址:https://angular-ui.github.io/bootstrap/)里的.后来因为各种原因,要换掉UI bootstrap中的日期控件,改用layDate日期控件. 解决思路:将layDate的初始化及相关代码定义在指令里. 问题关键点:layDate操作的是Html元素的,怎么实现双向绑定,同步Angularjs模板值和Html的元素值. 指

  • AngularJS自定义控件实例详解

    本文实例讲述了AngularJS自定义控件.分享给大家供大家参考,具体如下: 自定义指令介绍 AngularJS 指令作用是在 AngulaJS 应用中操作 Html 渲染.比如说,内插指令 ( {{ }} ), ng-repeat 指令以及 ng-if 指令. 当然你也可以实现自己的.这就是 AngularJS 所谓的"教会 HTML 玩新姿势".本文将告诉你如何做到. 指令类型 可以自定义的指令类型如下: 元素 属性 CSS class Comment directives 这里面

  • Angularjs上传图片实例详解

    •上传图片需要引入插件ngFileUpload,使用bower安装方法: bower install ng-file-upload --save,安装后需要在命名app的名字js文件中注入,如下所示: (function() { angular.module('app', [ 'ionic','ngStorage','ngFileUpload' ]); })(); •在相应的html中引入文件路径:<script src="lib/ng-file-upload/ng-file-upload

  • Angularjs Promise实例详解

    一.什么是Promise Promise是对象,代表了一个函数最终可能的返回值或抛出的异常,就是用来异步处理值的. Promise是一个构造函数,自己身上有all.reject.resolve这几个异步方式处理值的方法,原型上有then.catch等同样很眼熟的方法. 二.为什么使用Promise 有了Promise对象,就可以把异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数.此外,Promise对象提供了统一的接口,使得控制异步操作更加容易. Promise对象有以下2个特点: 1

  • AngularJS中使用three.js的实例详解

    AngularJS中使用three.js的实例详解 一.轨迹球的引入问题 一开始我是用下面的方式引如轨迹球,但是会报Trackballcontrols is undefined的错. import * as THREE from 'three'; import * as Trackballcontrols from 'three'; 但其实我是能够在node_module下的threejs的包中找到Trackballcontrols的文件的,我一开始以为是引用的路径没对然后修改路径到对应包下Tr

  • AngularJS中filter的使用实例详解

    AngularJS中filter的使用实例详解 一.格式化数字为货币格式. <div>{{money|currency:"$"}}</div> <div>{{money|currency:"RMB"}}</div> script: app.controller("crl", function($scope, $filter) { $scope.money="4545"; });

  • Angularjs中数据绑定的实例详解

    Angularjs中数据绑定的实例详解 这是一个最简单的angularjs的例子,关于数据绑定的,大家可以执行一下,看看效果 <html ng-app> <head> <title>angularjs-include</title> <script type="text/javascript" src="js/angular/angular.min.js"></script> </head

  • AngularJs表单验证实例详解

    常用的表单验证指令 1. 必填项验证 某个表单输入是否已填写,只要在输入字段元素上添加HTML5标记required即可: 复制代码 代码如下: <input type="text" required /> 2. 最小长度 验证表单输入的文本长度是否大于某个最小值,在输入字段上使用指令ng-minleng= "{number}": 复制代码 代码如下: <input type="text" ng-minlength="

  • Angularjs cookie 操作实例详解

    摘要 现在很多app采用内嵌h5的方式进行开发,有些数据会存在webveiw的cookie中,那么如果使用angularjs开发单页应用,就需要用到angularjs的cookie操作.这里提供一个简单的学习demo.方便快速上手. 一个例子 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" ng-app="myapp"> <head> <meta http

  • AngularJS tab栏实现和mvc小案例实例详解

    tab栏: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Tab 标签</title> <style> body { margin: 0; padding: 0; background-color: #F7F7F7; } .tabs { width: 400px; margin: 3

  • AngularJS中的拦截器实例详解

    AngularJS中的拦截器实例详解 异步操作 有时候需要在拦截器中做一些异步操作.幸运的是, AngularJS 允许我们返回一个 promise 延后处理.它将会在请求拦截器中延迟发送请求或者在响应拦截器中推迟响应. 下面是项目中用到的代码. ZbtjxcApp.factory('myHttpInterceptor', ['$q', '$window','$location', function($q, $window,$location) { return { // 全局响应 'respo

随机推荐