文档章节

AngularJs 控件小结

wonderzhou
 wonderzhou
发布于 2016/12/04 23:07
字数 3173
阅读 249
收藏 0
点赞 0
评论 0
  • AngularJs控件入门知识:

AngularJs自定义控件又叫自定义指令,自定义指令使用AngularJS扩展HTML的功能。通过添加自定义的特殊元素标签、标签属性等特殊标识来封装JavaScript代码,对AngularJs进行功能扩展,并且有利于代码的复用。常见的自定义指令形式如下:

  • Element directives - 指令遇到时激活一个匹配的元素。

  • Attribute - - 指令遇到时激活一个匹配的属性。

  • CSS - - 指令遇到时激活匹配CSS样式。

  • Comment - - 指令遇到时激活匹配的注释。

AngularJs控件的定义名称为directive,以下是一个基本的控件定义方法示例:

var mainApp = angular.module("mainApp", []);

//第一个参数是控件的自定义名称,当Html页面中出现“student”标识时,这个控件就会被激活。
mainApp.directive('student', function() {
   //定义控件对象
   var directive = {};
   //restrict = E, 表示这个控件是一个标签元素匹配型控件。
   directive.restrict = 'E';
   //template定义的模板标签将在定义的控件标签下自动加入,当replace属性定义为true时则会使用模板
   //替换掉控件标签。 
   directive.template = "Student: <b>{{student.name}}</b> , Roll No: <b>{{student.rollno}}</b>";\
   //如果设置为true,那么模版将会替换当前元素,而不是作为子元素添加到当前元素中。
   //(注:为true时,模版必须有一个根节点)
   directive.replace = true;
   //scope属性定义控件使用全局scope,还是使用一个隔离的局部的scope。
   directive.scope = {
       student : "=name"
   }
   //指令编译方法,在AngularJs初始化应用时执行,且只执行一次,用于操作、编译指令生成的Html。
   directive.compile = function(element, attributes) {
      element.css("border", "1px solid #cccccc");
	  //linkFunction 用于连接Html中需要赋值的参数,对参数进行赋值。
      var linkFunction = function($scope, element, attributes) {
          element.html("Student: <b>"+$scope.student.name +"</b> , Roll No: <b>"+$scope.student.rollno+"</b><br/>");
          element.css("background-color", "#ff00ff");
      }
      return linkFunction;
   }
   return directive;
});

 

更多指令详细介绍请访问:https://my.oschina.net/ilivebox/blog/289670

  • 几个AngularJs控件示例

  1. CSS控制指令
    <aps-button color="red"></aps-button>
    app.directive('apsButton', function() {
    
        var directive = {};
        directive.restrict = 'E';
        directive.template = "<button class='btn'>red</button>";
        directive.scope = {
            color : "=color"
        }
        directive.replace = true;
        directive.compile = function(element, attributes) {
            var linkFunction = function($scope, element, attributes) {
                element.css("background-color", attributes.color);
            }
            return linkFunction;
        }
        return directive;
    });

    在compile指令中利用element参数配置元素的CSS属性,可以通过获取自定义标签中的自定义属性来设置指定的样式。

  2. 带Label的input输入框指令
    <div class="form-group">
        <input aps-input label="节点名称" name="name" id="name" ng-model="node.name" />
        <span><strong>Your input value is : {{node.name}}</strong></span>
    </div>
    app.directive('apsInput', function() {
    	var directive = {};
    	directive.restrict = 'A';
    	directive.require = 'ngModel';
    	directive.transclude = true;
    	directive.compile = function(element, attributes) {
    		//获取指令属性中定义的label名称,并按照Bootstrap风格定义label标签。
    		var labelHtml = '<label class="control-label col-md-2 " for="'+attributes.label+'">'+attributes.label+':</label>';
    		var divHtml = '<div class="col-md-4 form-inline"></div>';
    		element.addClass("form-control col-md-2");
    		var linkFunction = function (scope, ele, attrs, ngModel) {
    			//将定义的HTML标签转换成元素。
    			var lableEle = angular.element(labelHtml);
    			var divEle = angular.element(divHtml);
    			//在input元素前插入label元素。
    			element.before(lableEle);
    			//在input元素外包装一个div元素。
    			element.wrap(divEle);
    			}
    		return linkFunction;
    	}
    	return directive;
    });

    生成带label的input控件重点在于element元素的操作;注意在添加DOM元素前需要将HTML标签用angular.element(); 转换成元素后再进行操作。

    关于element操作方法详情:http://www.th7.cn/web/html-css/201506/104549.shtml

  3. 输入框自定义验证指令

    AngularJs的验证主要发生在model 的值在前后台通过管道传递的过程中,下面先来介绍ngModel的在viewValue和modelValue之间的转换过程。

    ngmodel的转换过程

    angularjs将model和view之间的联系切断,自己内部通过ng-model去实现ViewModel层。这里写图片描述

    在每个使用ng-model的地方,都会创建一个ngModelController实例,这个实例负责管理存储在模型(由model指定)中的值与元素显示值之间的数据绑定。

    ngModelController包含有$formatters和$parsers数组,会在每次更新数据绑定时调用。

    当我们从页面或通过$setViewValue改变绑定的属性时,会遍历执行$parsers数组里面的方法;而当我们直接在js里面通过赋值语句修改model的值时,则会调用$formatters函数数组。详见:1、http://stackoverflow.com/questions/22841225/ngmodel-formatters-and-parsers
    2、http://www.cnblogs.com/whitewolf/p/angular-input-box-format.html

    而我们所需要定义的验证逻辑也是加在这些函数中。

    <div class="form-group">
       <label class="col-md-2 control-label">用户名验证:</label>
       <div class="col-md-2 form-inline">
          <input name-validator class="form-control" ng-model="userName" name="user" id="user"/>
       </div>
       <span>校验结果为:{{vform.user}}</span>
    </div>
    app.directive("nameValidator", function(){
        var directive = {};
        directive.restrict = 'A';
        directive.require = 'ngModel';
        directive.link = function($scope, $element, $attrs, $ngModelCtrl) {
            //验证规则为不能全为数字、大写字母、小写字母。
            var verifyRule = [/^\d+$/, /^[a-z]+$/, /^[A-Z]+$/];
            var verify = function(input) {
                return !(verifyRule[0].test(input) ||
                verifyRule[1].test(input) ||
                verifyRule[2].test(input));
            };
            
            $ngModelCtrl.$parsers.push(function(input) {
                var validity = verify(input);
                $ngModelCtrl.$setValidity('defined', validity);
                return validity ? input : undefined;
            });
            $ngModelCtrl.$formatters.push(function(input) {
                var validity = verify(input);
                $ngModelCtrl.$setValidity('defined', validity);
                return  input ;
            })
        };
        return directive;
    });

    以上代码是一个用户名验证自定义控件,验证规则为不能全为数字、大写字母、小写字母。

    在$parsers中定义了一个由viewValue到modelValue时执行的函数,执行过程中会将验证结果通过$ngModelCtrl.$setValidity('defined', validity);语句保存;当验证通过时返回原值,不通过时返回undefined。

    在$formatters中也定义了一个model值传到view时执行的函数,同样会保存验证结果;直接返回原值到view;

  4. echarts封装指令

    AngularJs控件同样适用于封装一些图表组件。下面以百度echarts为例介绍数据图的封装。

    <div class="row">
       <div class="col-md-12">
           <line id="main" legend="legend" item="item" data="data" theme="'macarons'" unit="'次'"></line>
       </div>
    </div>
    app.directive("line", function(){
        var directive = {};
        directive.restrict = 'E';
        directive.scope = {
            id: "@",
            legend: "=",
            item: "=",
            data: "=",
            theme: "=",
            unit: "="
        };
        directive.template = "<div style='height: 400px;'></div>";
        directive.replace = true;
        directive.link = function ($scope, element, attrs, controller) {
            var option = {
                // 提示框,鼠标悬浮交互时的信息提示
                tooltip: {
                    trigger: 'axis'
                },
                // 图例
                legend: {
                    data: $scope.legend
                },
                // 横轴坐标轴
                xAxis: [{
                    type: 'category',
                    data: $scope.item
                }],
                // 纵轴坐标轴
                yAxis: [{
                    type: 'value',
                    axisLabel : {
                        formatter: '{value} ' + $scope.unit
                    }
                }],
                // 数据内容数组
                series: function(){
                    var serie=[];
                    for(var i=0;i<$scope.legend.length;i++){
                        var item = {
                            name : $scope.legend[i],
                            type: 'line',
                            data: $scope.data[i]
                        };
                        serie.push(item);
                    }
                    return serie;
                }()
            };
            var myChart = echarts.init(document.getElementById($scope.id), $scope.theme);
            myChart.setOption(option);
        };
        return directive;
    });
      $scope.legend = ["请求数", "响应数"];
      $scope.item = ["2016-11-01", "2016-11-02", "2016-11-03", "2016-11-04", "2016-11-05", "2016-11-06", "2016-11-07", "2016-11-08", "2016-11-09", "2016-11-10", "2016-11-11", "2016-11-12"];
      $scope.data = [
        ["0", "46", "0", "23", "33", "0", "50", "0", "0", "78", "37", "70"],
        ["0", "40", "0", "23", "63", "0", "0", "32", "0", "53", "0", "35"]
      ];

    echarts的封装配置方面很简单,重点在于数据结构的整理,需要数据按照它的要求进行整理并赋值。

  5. 外部指令封装

    Angular同样可以进行控件的嵌套封装。下面通过演示Kendo UI中的日期选择控件来介绍控件的嵌套封装。

    <div class="form-horizontal form-group">
        <label for="time" class="control-label col-md-2 col-md-offset-2">请选择日期:</label>
        <div class="col-md-2 form-inline">
            <aps-date-picker id="time" model="dateString" style="width:100%;"></aps-date-picker>
        </div>
        <div class="col-md-2 form-inline">
            <span>你选择的日期是:{{dateString}}</span></div></br>
        </div>
    </div>
    app.directive("apsDatePicker", function($compile){
       var directive = {};
        kendo.culture("zh-CN");//kendo控件的语言设置
        directive.restrict = "E";
        directive.replace = true;
    	directive.require = "ngModel";
        directive.template = '<input kendo-date-picker ng-model="dateStr" style="width: 100%;" />';
        directive.link = function(scope, element, attrs, ngModel) {
            //当日期已经预先定义了时,需要将值传递到日期控件中
            if(scope[attrs['model']]){
            	ngModel.$setViewValue(scope[attrs['model']]);
            }
            //监听方法,用于监控日期选择后的值变化,将新值更新到model中
            scope.$watch('dateStr', function(newVal){
            	scope[attrs['model']] = newVal;
            });
        }
        return directive;
    });
    

    控件的嵌套封装重点在于自定义model值和原控件值的同步;需要加入合适的监听方法,在原控件值更新时,将自定义model值同时更新。

    两种监听方法($watch, $observe)简介:
        $observe和$watch都可以用来对属性进行监控;
        但$observe方法是一个异步解析的操作,是在其他表达式都已经解析之后再解析,这样使它拥有了处理像{{}}插值字符串的机会。$observe是属性对象上的方法,因此它是用来监控DOM属性上的值的变化,它仅用在指令内部,当你需要在指令内部监控包含有插值表达式的DOM属性的时候,就要用到这个方法。
        $watch更复杂一点,它可以监视表达式,这个表达式可以是函数或者字符串,假如表达式是字符串的话,会被封装成一个函数,然后在digest循环的时候被调用。 这个字符串表达式不能包含{{}},$watch是一个scope对象上的方法,所以它可以在任何你可以访问到作用域的地方被调用。比如,控制器中或者link函数中。因为字符串是被当做angular的表达式解析的,所以$watch经常被用在当你想要监控一个模型或者作用域对象的时候。
    详细介绍:http://www.ngnice.com/posts/2314014da4eea8

:directive中controller、compile和 link函数的区别:

  1. 这个3个函数都有element参数,都可以操作自定义指令标签。
  2. controller在编译前运行,主要用于处理为呈现视图之前而准备的数据或者是与其他指令进行数据交互,目的是为了指令间进行交流。当一个指令需要使用另一个指令的controller进行数据交互,则在require参数中设置该controller即可。
  3. compile函数主要用于对template中的元素进行诸如添加和删除节点等DOM操作,compile函数仅在指令初始化时运行一次,而link函数则主要负责将scope中的数据与DOM进行链接,每个指令的实例都会运行link函数。这里需要注意的是,compile函数和link函数在directive下是不能同时定义的,否则只会运行compile函数,想要同时使用两者,只能在compile函数中将link函数作为返回值返回;而link函数中的element对象即是compile函数对template元素进行处理后的实例元素,同时还包含了一个scope对象。
  4. 在compile函数中的返回值中,可以定义preLink和postLink两种link函数返回。当只定义一种link函数时,默认为postLink函数。而当link函数中出现指令嵌套(指令DOM下包含指令)时,preLink函数会从外到内依次执行而postLink则会从子指令到外,反向执行,这样可以确保在父指令标签执行时,子指令是已经运行过的。所以postLink是最安全或者默认的写业务逻辑的地方。详见:http://www.jb51.net/article/58229.htm

        

小结:

controller主要用作数据的准备和指令间数据交流。
compile仅在指令初始化时运行一次,用于操作模板,准备DOM。
Link函数用于compile之后与scope进行数据绑定时调用,是指令逻辑最常写入的地方。
另外,在指令定义之外也可以使用Link,例如构造弹出框时,将页面标签使用var linkFn = $compile(angular.element(tpl));命令进行编译,然后linkFn(scope),即可将scope与页面元素进行绑定。

需要注意的点:

  1. Interpolated attributes (i.e., attributes that use {{}}s) and isolate scope properties defined with '@' are not available when the link function runs. You need to use attrs.$observe() (or scope.$watch( @ property here, ...)) to get the values (asynchronously). ---摘自StackOverFlow  http://stackoverflow.com/questions/15438837/nesting-directives-within-directives

    翻译:差值属性(带{{}}的属性)和使用‘@’定义的隔离scope属性在link时是不可用的。如果要使用它们的值,可以使用attrs.$observe() (或 scope.$watch( @ property here, ...))来异步的获取。

  2. $parse/$eval的区别
    $parse跟$eval都是用来解析表达式的, 但是$parse是作为一个单独的服务存在的。

    var getter = $parse('user.name'); 
    var setter = getter.assign; 
    getter(context, locals) // 传入作用域,返回值
    setter(scope,'new name') // 修改映射在scope上的属性的值为‘new value’

    $eval是作为scope的方法来使用的。是执行当前作用域下的表达式。$eval的内部实现实际使用的就是$parse,只不过作用域设置为当前作用域scope了。

    $eval: function(expr, locals) {
        return $parse(expr)(this, locals);
    },

    参考:http://www.ngnice.com/posts/2314014da4eea8

  • 总结

编写Angularjs控件时,需要注意区分不同的函数的特性:编译时间、编译顺序、编译结果以及函数的用途。例如controller、compile、link函数;

另外还需注意函数中各个参数的正确使用,熟悉参数中的属性内容对提高代码的简洁度有很大帮助。

在编写控件之前最好对Angularjs控件函数有较为清晰的认知,最好对照官方文档学习和使用各个函数,增强熟练度。

  • Angularjs博客参考:

Angularjs开发一些经验总结: http://www.cnblogs.com/whitewolf/archive/2013/03/24/2979344.html

AngularJs input格式化: http://www.cnblogs.com/whitewolf/p/angular-input-box-format.html

Angular实例讲解: http://blog.51yip.com/jsjquery/1607.html


 

© 著作权归作者所有

共有 人打赏支持
wonderzhou
粉丝 1
博文 2
码字总数 3173
作品 0
深圳
程序员
Angular 6.0正式版发布,都有哪些新功能

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 在Angular 5发布半年之后,Angular 6在昨天正式发布,那么在这个版本有哪些新功能呢?新版本重点关注工具链以及工具链在...

异步社区 ⋅ 05/08 ⋅ 0

Angular 6正式版发布,都有哪些新功能

在Angular 5发布半年之后,Angular 6在昨天正式发布,那么在这个版本有哪些新功能呢?新版本重点关注工具链以及工具链在 Angular 中的运行速度问题。除此之外,这次更新还包括框架包(@angu...

code_xzh ⋅ 05/05 ⋅ 0

[Angular Material完全攻略] Day 02 - 环境设定 & 安装 & Hello World

今天我们将开始正式迈入Angular Material的世界,要学习使用Angular Material打造高品质及高质感的网页,当然要从安装Angular Material套件开始,本篇文章就来介绍基本的Angular Material安装...

readilen ⋅ 05/21 ⋅ 0

[Angular Material完全攻略] Day 01 - 开始 & 简介

转载 从Angular第2版正式release后,根据全球最大工程师讨论区StackOverflow的统计,从2016开始的Angular讨论度就不断窜升,甚至超越了React,直到了2017年,甚至摆脱了前一代Angularjs的阴影...

readilen ⋅ 05/21 ⋅ 0

【Angular】之Angular环境搭建

前言 小编最近在学习angular的内容,万事开头难,完成了开头也便成功了一半。这句话小编在这次的学习实践之旅中,深有体会,下面小编就来讲一讲,angular环境搭建之旅吧! 一、环境搭建 1.下...

m18633778874 ⋅ 05/08 ⋅ 0

【前端】—聊聊我认识的Angular

前言 最近接触的项目前端用到了Angular框架,之前略有耳闻,从vue换到Angular,感觉东西差不多,还是要系统学习的,先来了解下。 正文 1、Angular 的发展 AngularJS 是一款来自Google的前端J...

zt15732625878 ⋅ 05/19 ⋅ 0

初学angular 看到网上有angular js 也有angular2 ,到angular官网发现最近版本是6了,那么现在大家说的angular js到底是什么啊?

初学angular 看到网上有angular js 也有angular2 ,到angular官网发现最近版本是6了,那么现在大家说的angular js到底是什么啊? angular2和现在官网的angular6 就是 angular js 只是版本不同...

Jordan裔 ⋅ 05/19 ⋅ 0

AngularJS 的 Material Design 风格框架 - AngularJS Material

AngularJS Material 是 AngularJS 框架的谷歌 Material Design 标准的实现。AngularJS Material 包含一组丰富的、可重用、经过充分测试并可访问的 UI 组件。 针对 Angular 2 或更高版本的实现...

匿名 ⋅ 05/15 ⋅ 0

勿以 star 数论高低!React 在前端框架世界仍是领先地位

上周在前端的 JavaScript 框架世界发生了一件称得上是“里程碑”的事:Vue.js 在 GitHub 上的 star 数量终于超过了 React.js 的 star 数量。 通过使用一个叫做 Star History 的工具,我们可以...

局长 ⋅ 前天 ⋅ 0

Angular 的 Material Design 风格框架 - Angular Material

Material Design for Angular 是 Angular 官方团队开发的基于最新版本 Angular 的 Material Design 风格的框架,可和 Nest.js 搭配使用做全栈开发。 针对 Angular 1 版本的实现 https://www....

匿名 ⋅ 05/15 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vbs 取文件大小 字节

dim namedim fs, s'name = Inputbox("姓名")'msgbox(name)set fs = wscript.createobject("scripting.filesystemobject") 'fs为FSO实例if (fs.folderexists("c:\temp"))......

vga ⋅ 7分钟前 ⋅ 0

高并发之Nginx的限流

首先Nginx的版本号有要求,最低为1.11.5 如果低于这个版本,在Nginx的配置中 upstream web_app { server 到达Ip1:端口 max_conns=10; server 到达Ip2:端口 max_conns=10; } server { listen ...

算法之名 ⋅ 今天 ⋅ 0

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部