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 相关文章推荐
javascript add event remove event
Apr 07 Javascript
判断iframe是否加载完成的完美方法
Jan 07 Javascript
js和jquery对dom节点的操作(创建/追加)
Apr 21 Javascript
jquery 插件实现多行文本框[textarea]自动高度
Mar 04 Javascript
JS简单实现仿百度控制台输出信息效果
Sep 04 Javascript
详解JS获取HTML DOM元素的8种方法
Jun 17 Javascript
JS实现的ajax和同源策略(实例讲解)
Dec 01 Javascript
详解Vue iview IE浏览器不兼容报错(Iview Bable polyfill)
Jan 07 Javascript
RxJS在TypeScript中的简单使用详解
Apr 13 Javascript
基于原生js实现判断元素是否有指定class名
Jul 11 Javascript
nuxt 实现在其它js文件中使用store的方式
Nov 05 Javascript
微信小程序自定义支持图片的弹窗
Dec 21 Javascript
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
星际争霸 Starcraft 秘技补丁
2020/03/14 星际争霸
thinkPHP使用post方式查询时分页失效的解决方法
2015/12/09 PHP
CodeIgniter配置之routes.php用法实例分析
2016/01/19 PHP
PHP实现活动人选抽奖功能
2017/04/19 PHP
小型js框架veryide.librar源代码
2009/03/05 Javascript
javascript获得网页窗口实际大小的示例代码
2013/09/21 Javascript
js data日期初始化的5种方法
2013/12/29 Javascript
javascript不同类型数据之间的运算的转换方法
2014/02/13 Javascript
js 事件截取enter按键页面提交事件示例代码
2014/03/04 Javascript
JS+CSS实现带关闭按钮DIV弹出窗口的方法
2015/02/27 Javascript
javascript三元运算符用法实例
2015/04/16 Javascript
原生JS和JQuery动态添加、删除表格行的方法
2015/05/28 Javascript
js实现上一页下一页的效果【附代码】
2016/03/10 Javascript
浅谈JavaScript的push(),pop(),concat()方法
2016/06/03 Javascript
xcode中获取js文件的路径方法(推荐)
2016/11/05 Javascript
基于javascript实现按圆形排列DIV元素(三)
2016/12/02 Javascript
Node.js连接MongoDB数据库产生的问题
2017/02/08 Javascript
Vue表单验证插件的制作过程
2017/04/01 Javascript
vue实现点击隐藏与显示实例分享
2019/02/13 Javascript
深入了解JavaScript 私有化
2019/05/30 Javascript
小程序click-scroll组件设计
2019/06/18 Javascript
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
Python统计列表中的重复项出现的次数的方法
2014/08/18 Python
Python的Django框架下管理站点的基本方法
2015/07/17 Python
深入理解Python中变量赋值的问题
2017/01/12 Python
Python实现字典按照value进行排序的方法分析
2017/12/23 Python
python批量替换页眉页脚实例代码
2018/01/22 Python
特征脸(Eigenface)理论基础之PCA主成分分析法
2018/03/13 Python
python2使用bs4爬取腾讯社招过程解析
2019/08/14 Python
Python解释器以及PyCharm的安装教程图文详解
2020/02/26 Python
html5教程画矩形代码分享
2013/12/04 HTML / CSS
Notino法国:购买香水和化妆品
2019/04/15 全球购物
娱乐地球:Entertainment Earth
2020/01/08 全球购物
人力资源经理自我评价
2014/01/04 职场文书
八项规定整改方案
2014/10/01 职场文书
《和时间赛跑》读后感3篇
2019/12/16 职场文书