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 相关文章推荐
使用node.js半年来总结的 10 条经验
Aug 18 Javascript
纯javascript代码实现计算器功能(三种方法)
Sep 07 Javascript
JS实现的验证身份证及获取地区功能示例
Jan 16 Javascript
JavaScript设计模式之代理模式详解
Jun 09 Javascript
JavaScript ES6中const、let与var的对比详解
Jun 18 Javascript
Vue2.0权限树组件实现代码
Aug 29 Javascript
Vue表单类的父子组件数据传递示例
May 03 Javascript
JS 实现获取验证码 倒计时功能
Oct 29 Javascript
关于Layui Table隐藏列问题
Sep 16 Javascript
layui 上传图片 返回图片地址的方法
Sep 26 Javascript
vue 动态设置img的src地址无效,npm run build 后找不到文件的解决
Jul 26 Javascript
vite+vue3.0+ts+element-plus快速搭建项目的实现
Jun 24 Vue.js
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
PHP 和 HTML
2006/10/09 PHP
PHP调用三种数据库的方法(1)
2006/10/09 PHP
一个连接两个不同MYSQL数据库的PHP程序
2006/10/09 PHP
URL Rewrite的设置方法
2007/01/02 PHP
php使用APC实现实时上传进度条功能
2015/10/26 PHP
php调用自己java程序的方法详解
2016/05/13 PHP
php查询及多条件查询
2017/02/26 PHP
PHP7使用ODBC连接SQL Server2008 R2数据库示例【基于thinkPHP5.1框架】
2019/05/06 PHP
JS实现字体选色板实例代码
2013/11/20 Javascript
JS实现仿京东淘宝竖排二级导航
2014/12/08 Javascript
node.js中的fs.mkdirSync方法使用说明
2014/12/17 Javascript
JavaScript中的函数嵌套使用
2015/06/04 Javascript
js实现的后台左侧管理菜单代码
2015/09/11 Javascript
javascript制作照片墙及制作过程中出现的问题
2016/04/04 Javascript
Javascript发送AJAX请求实例代码
2016/08/21 Javascript
Node.js中看JavaScript的引用
2017/04/22 Javascript
VueJs 将接口用webpack代理到本地的方法
2017/11/27 Javascript
vue学习教程之带你一步步详细解析vue-cli
2017/12/26 Javascript
webpack之devtool详解
2018/02/10 Javascript
JavaScript实现的反序列化json字符串操作示例
2018/07/18 Javascript
jQuery实现鼠标移到某个对象时弹出显示层功能
2018/08/23 jQuery
详解小程序不同页面之间通讯的解决方案
2018/11/23 Javascript
关于node-bindings无法在Electron中使用的解决办法
2018/12/18 Javascript
移动端(微信等使用vConsole调试console的方法
2019/03/05 Javascript
详解Vue-cli3 项目在安卓低版本系统和IE上白屏问题解决
2019/04/14 Javascript
[52:07]完美世界DOTA2联赛PWL S3 LBZS vs access 第二场 12.10
2020/12/13 DOTA
Python通过90行代码搭建一个音乐搜索工具
2015/07/29 Python
Python入门_浅谈字符串的分片与索引、字符串的方法
2017/05/16 Python
python实现关闭第三方窗口的方法
2019/06/28 Python
Django REST 异常处理详解
2020/07/15 Python
运行python提示no module named sklearn的解决方法
2020/11/29 Python
荷兰多品牌网上鞋店:Stoute Schoenen
2017/08/24 全球购物
洲际酒店集团美国官网:IHG美国
2017/11/16 全球购物
运动会跳远广播稿5篇
2014/09/17 职场文书
学雷锋日活动总结
2015/02/06 职场文书
MySQL数据库实验之 触发器和存储过程
2022/06/21 MySQL