AngularJS封装指令方法详解


Posted in Javascript onDecember 12, 2016

本文实例讲述了AngularJS封装指令方法。分享给大家供大家参考,具体如下:

引言:angularjs是一个中等重量级的前端开发框架

HTML是一门很好的为静态文本设计的语言,但要构建动态的web应用它就显的乏力了。通常,我们使用以下技术来解决静态网页技术在构建动态应用上的不足:

1.类库:类库是一类函数的集合,它能帮助你写web应用。这里起主导作用是你的代码,由你来决定何时使用类库。典型的类库,例如prototype、jQuery等。

2.框架:框架式一种特殊的、已经实现的web应用,你只需要填充具体的业务逻辑。这里框架是起主导作用的,由它根据具体的逻辑来调用你的代码。典型的框架例如knockout,sproutcore, YUI等。AngularJS也是其中之一。

框架又有轻重之分。我对轻重的判断标准是,是否需要很多的第三方类库来帮助你实现功能。显然,backbone这种属于轻量级框架,它简单易用,专注于前端Mvc的实现,故而你还需要很多第三方类库(至少jquery)来完成dom操作、UI等各种各样的内容。Yui、dojo属于重型框架,他们的作者企图搞出一个森罗万象的框架+组件库,包括代码动态调用、各种UI组件都包含在内,学习成本较高,但是一旦精通,至少这个项目别无所求。从这个角度讲,轻量级框架好比毛坯房,还需要各种工具做装修,但是对于开发者来说也更灵活。重量级框架好比精装修的房间,你只需要的是适应它,但如果要自己做出大刀阔斧的修改,那就稍微有点伤经动骨了。

angularjs,在我看来是介于以上两类之间,是个中等重量级的框架。即不像backbone那么简单,也不像dojo和Yui那么包罗万象。很多时候,妄图包罗万象,往往会出现很多子模块的质量高不成低不就,并且修改起来较为困难。过分精简,则框架内容单薄需要写的内容太多。angularjs这种相对中庸的风格,则非常符合我的需求。目前,AngularJS三个我认为最为精妙的组件就是数据绑定(Scope),指令(Directive)和依赖注入(Dependency Injection),表现得非常好。相对而言,它的UI组件和动画则是弱项。可以说,选择了angularjs,就意味着选择了jquery式的组件库方式来弥补它的不足,要完成一个web应用必须跟第三方类库打交道。

现在已经有许多针对angularjs写的UI插件,有的是结合了bootstrap,有的是结合了jquery, 虽然不太完善,都很值得参考:http://angular-ui.github.io/

与jquery类库的协作

第三方类库中,不得不提的是大名鼎鼎的jquery,现在基本上已经是国内web开发的必修工具了。它灵活的dom操作,让很多web开发人员欲罢不能。再加上已经很成熟的jquery UI 库和大量jquery 插件,几乎是一个取之不尽用之不竭的宝库。然而,它是否能与angularjs结合呢?

很多angularjs原教旨主义者对此持否定态度。他们认为,既然已经使用了angularjs做web应用框架,那就必须避免其他类库的干扰,做纯净的MvvM模式应用。任何类似jquery的dom操作,都是不洁的。把所有和界面相关的, 比如dom操作, 都放在directive中, 这样页面中directive而没有代码,跟JSF思想一致。MVVM,DSL,组件化的思想这才是web的趋势。嗯,想法很好,原教旨主义者想法都是这么纯洁。但事实情况是,使用了angularjs我们就离不开jquery。

众所周知,angularjs里面事实上已经内置了jquery lite.,而且angularjs源码中很多方法直接就是使用jquery方法。例如angularjs的事件绑定机制。既然先知们都在用,我们又何苦不用?组件化的思想没有错,但没必要因此把自己的手脚绑住。唯一要注意的问题是,不要用jquery的代码破坏了angularjs的结构。对此我的原则如下,不足之处还请指出:

模块划分、服务、路由、依赖注入等重要方面上都得使用angularjs的方式,只有某些具体内容(通常是一些Ui)才使用jquery。 避免在controller里面写了一堆直接操作dom元素的 jquery代码。使用angularjs的模板绑定机制。 常用的组件要用angularjs的方法抽取出来,但组件具体实现则不必纠结于是否使用jquery及其插件。 .使用第三方类库时,在变量和函数命名时有特殊标记(通常是加上这个类库名的缩写)。

jquery,更是建议作为angularjs的依赖,先于angularjs加载进来。

事实上,选择了angularjs这样的框架中德中等重量级选手,就意味着你必须添加其他类库。而jquery,更是建议作为angularjs的依赖,先于angularjs加载进来。因为在查看angularjs API的时候,我已经发现,其中许多功能,事实上是依赖于jquery的。典型的例子,就是官网的ng-blur指令。

<input type="text" ng-model="name" ng-blur="checkname()" > ng-blur指令,是在焦点离开某个元素时触发的指令。对于上例,即在焦点离开该文本输入框时,触发checkname()函数。

看起来很简单,但是你如果真的使用了这个指令,你就会发现它根本不起效果。在仔细查看文档后,我才发现这实际是先知们使用jquery的blur方法实现的函数(而且事实上根本没有真正实现并放在当前的版本里)。那么就算我们想写一个,离开jquery原生库是不行的,因为blur方法并未封装到angularjs内带的jquery lite里。换句话说,必须先载入完整的jquery才能使用。于是,我干脆自己写了一个标签:

/*
* angular directive onBlur
*
* @description my ng-blur
* @require jquery
*/
$compileProvider.directive('onBlur', function() {
  return {
    restrict : 'A',
    link : function(scope, elm, attrs) {
      elm.bind('blur', function() {
        scope.$apply(attrs.onBlur);
      });
    }
  };
});

这已经很好了。

但是还不够完美。由于$apply方法接受函数的问题,所以直接像上面这样写,有可能导致angularjs运行时报错:$apply already in progress

避免这个问题的发生,则需要对$apply方法进行加工:

/* factory function safeApply
*
* @description If you find yourself triggering the '$apply already in progress' error while developing with Angular.JS
* (for me I find I hit most often when integrating third party plugins that trigger a lot of DOM events),
* you can use a 'safeApply' method that checks the current phase before executing your function.
*
* @param scope, the action scope, mostly is the topmost controller
* @param fn, the function which you want to apply into scope
* @see https://coderwall.com/p/ngisma
*/.factory('safeApply', function($rootScope) {
  return function(scope, fn) {
    var phase = scope.$root.$$phase;
    if (phase == '$apply' || phase == '$digest') {
      if (fn && ( typeof (fn) === 'function')) {
        fn();
      }
    } else {
      scope.$apply(fn);
    }
  }
});

那么之前的onblur标签,就应该改为:

/*
* angular directive onBlur
*
* @description my ng-blur
* @require jquery
*/
$compileProvider.directive('onBlur', function(safeApply) {
  return {
    restrict : 'A',
    link : function(scope, elm, attrs) {
      elm.bind('blur', function() {
        safeApply(scope, attrs.onBlur);
      });
    }
  };
});

以上代码我已经加入了自己的angular_extend模块,在自己的项目中使用了,效果很好。

将jquery 插件用angularjs的方式封装成组件的例子

icheck是一个jquery插件,用于跨浏览器美化Checkbox和Radio按纽。关于它的介绍,在http://www.bootcss.com/p/icheck/

一般来说,它的使用方法是在dom载入后加一段jquery代码:

$('input').iCheck({
  labelHover : false,
  cursor : true,
  checkboxClass : 'icheckbox_square-blue',
  radioClass : 'iradio_square-blue',
  increaseArea : '20%'
});

但是既然要放在我们的项目里,就不能到处塞这种直接操作dom的jquery代码,既不美观,也不易维护。按照之前所说的原则,最好将其封装成angular指令的模式,放在公共模块里来调用。这里我将我新建的指令命名为ng-icheck。如此,我们只要写在某个checkbox或者radio的html标签里加上一句ng-ickeck即可。具体实现如下:

/*
 * angular directive ng-icheck
 *
 * @description icheck is a plugin of jquery for beautifying checkbox & radio, now I complied it with angular directive
 * @require jquery, icheck
 * @example <input type="radio" ng-model="paomian" value="kangshifu" ng-icheck>
 *     <input type="checkbox" class="icheckbox" name="mantou" ng-model="mantou" ng-icheck checked>
 */
$compileProvider.directive('ngIcheck', function($compile) {
  return {
    restrict : 'A',
    require : '?ngModel',
    link : function($scope, $element, $attrs, $ngModel) {
      if (!$ngModel) {
        return;
      }
      //using iCheck
      $($element).iCheck({
        labelHover : false,
        cursor : true,
        checkboxClass : 'icheckbox_square-blue',
        radioClass : 'iradio_square-blue',
        increaseArea : '20%'
      }).on('ifClicked', function(event) {
        if ($attrs.type == "checkbox") {
          //checkbox, $ViewValue = true/false/undefined
          $scope.$apply(function() {
            $ngModel.$setViewValue(!($ngModel.$modelValue == undefined ? false : $ngModel.$modelValue));
          });
        } else {
          // radio, $ViewValue = $attrs.value
          $scope.$apply(function() {
            $ngModel.$setViewValue($attrs.value);
          });
        }
      });
    },
  };
});

在以上代码中值得注意的是:使用了icheck插件后,会生成一个美化过的div覆盖在原来的checkbox或者radio之上,而原来的checkbox或者radio会被影藏。故而,当我们点击它们时,不会直接触发事件,使得绑定到checkbox或者radio上的model值改变。所以我们这里需要重新绑定事件,使用

$ngModel.$setViewValue() 方法来给model赋值。具体逻辑,则相根据checkbox和radio而不同。详见以上代码。

由于以上代码写在我的项目中的通用模块common_angular_component.js里,故而在调用了该通用模块的页面里,直接使用ng-icheck指令即可实现ickeck的美化效果,同时避免了大量重复的jquery代码的出现。

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

Javascript 相关文章推荐
setInterval,setTimeout与jquery混用的问题
Apr 08 Javascript
gulp-htmlmin压缩html的gulp插件实例代码
Jun 06 Javascript
AngularJS入门教程之过滤器详解
Aug 19 Javascript
详解Angularjs 如何自定义Img的ng-load 事件
Feb 15 Javascript
Webpack+Vue如何导入Jquery和Jquery的第三方插件
Feb 20 Javascript
jQuery简单实现MD5加密的方法
Mar 03 Javascript
ES6解构赋值实例详解
Oct 31 Javascript
如何更好的编写js async函数
May 13 Javascript
js使用swiper实现层叠轮播效果实例代码
Dec 12 Javascript
vue实现select下拉显示隐藏功能
Sep 30 Javascript
JS实现灯泡开关特效
Mar 30 Javascript
jquery检测上传文件大小示例
Apr 26 jQuery
JS+Canvas实现的俄罗斯方块游戏完整实例
Dec 12 #Javascript
最好用的Bootstrap fileinput.js文件上传组件
Dec 12 #Javascript
jQuery Ajax File Upload实例源码
Dec 12 #Javascript
VueJs与ReactJS和AngularJS的异同点
Dec 12 #Javascript
layer实现弹窗提交信息
Dec 12 #Javascript
详解Javascript数据类型的转换规则
Dec 12 #Javascript
设置jquery UI 控件的大小方法
Dec 12 #Javascript
You might like
77A一级收信机修理记
2021/03/02 无线电
PHP下利用header()函数设置浏览器缓存的代码
2010/09/01 PHP
ThinkPHP无限级分类原理实现留言与回复功能实例
2014/10/31 PHP
PHP+JS三级菜单联动菜单实现方法
2016/02/24 PHP
js文件中调用js的实现方法小结
2009/10/23 Javascript
jquery学习笔记二 实现可编辑的表格
2010/04/09 Javascript
jquery中交替点击事件toggle方法的使用示例
2013/12/08 Javascript
javascript学习笔记(七)Ajax和Http状态码
2014/10/08 Javascript
js通过iframe加载外部网页的实现代码
2015/04/05 Javascript
jQuery带时间的日期控件代码分享
2015/08/26 Javascript
基于jquery实现瀑布流布局
2020/06/28 Javascript
js组件SlotMachine实现图片切换效果制作抽奖系统
2016/04/17 Javascript
JavaScript定时器制作弹窗小广告
2017/02/05 Javascript
每周一练 之 数据结构与算法(Stack)
2019/04/16 Javascript
Nuxt使用Vuex的方法示例
2019/09/06 Javascript
layui 实现二级弹窗弹出之后 关闭一级弹窗的方法
2019/09/18 Javascript
使用npm命令提示: 'npm' 不是内部或外部命令,也不是可运行的程序的处理方法
2020/05/14 Javascript
如何HttpServletRequest文件对象并储存
2020/08/14 Javascript
[58:21]DOTA2亚洲邀请赛 4.3 突围赛 Liquid vs VGJ.T 第二场
2018/04/04 DOTA
使用rst2pdf实现将sphinx生成PDF
2016/06/07 Python
Python 模拟购物车的实例讲解
2017/09/11 Python
Python针对给定列表中元素进行翻转操作的方法分析
2018/04/27 Python
Python 解决中文写入Excel时抛异常的问题
2018/05/03 Python
numpy matrix和array的乘和加实例
2018/06/28 Python
Tensorflow中的placeholder和feed_dict的使用
2018/07/09 Python
python 实现对数据集的归一化的方法(0-1之间)
2018/07/17 Python
简单了解python gevent 协程使用及作用
2019/07/22 Python
django Admin文档生成器使用详解
2019/07/22 Python
Python绘制热力图示例
2019/09/27 Python
Python selenium的基本使用方法分析
2019/12/21 Python
python使用numpy实现直方图反向投影示例
2020/01/17 Python
Django 如何使用日期时间选择器规范用户的时间输入示例代码详解
2020/05/22 Python
美国首屈一指的礼品篮供应商:GiftTree
2018/01/06 全球购物
Wedgwood美国官网:英国骨瓷,精美礼品及家居装饰
2018/02/17 全球购物
学生爱国演讲稿
2014/01/14 职场文书
观看《杨善洲》宣传教育片心得体会
2016/01/23 职场文书