angularJs项目实战!03: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指令。
1
|
〈input type="text" ng-model="name" ng-blur="checkname()" 〉
|
ng-blur指令,是在焦点离开某个元素时触发的指令。对于上例,即在焦点离开该文本输入框时,触发checkname()函数。
看起来很简单,但是你如果真的使用了这个指令,你就会发现它根本不起效果。在仔细查看文档后,我才发现这实际是先知们使用jquery的blur方法实现的函数(而且事实上根本没有真正实现并放在当前的版本里)。那么就算我们想写一个,离开jquery原生库是不行的,因为blur方法并未封装到angularjs内带的jquery lite里。换句话说,必须先载入完整的jquery才能使用。于是,我干脆自己写了一个标签:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/*
* 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方法进行加工:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/* 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标签,就应该改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/*
* 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代码:
1
2
3
4
5
6
7
|
$('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即可。具体实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/*
* 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值改变。所以我们这里需要重新绑定事件,使用
1
|
$ngModel.$setViewValue()
|
方法来给model赋值。具体逻辑,则相根据checkbox和radio而不同。详见以上代码。
由于以上代码写在我的项目中的通用模块common_angular_component.js里,故而在调用了该通用模块的页面里,直接使用ng-icheck指令即可实现ickeck的美化效果,同时避免了大量重复的jquery代码的出现。
1
2
|
<input type="radio" ng-model="paomian" value="kangshifu" ng-icheck>
<input type="checkbox" name="mantou" ng-model="mantou" ng-icheck checked>
|
angularJs项目实战!03:angularjs与其他类库的协作的更多相关文章
- angularJs项目实战!02:前端的页面分解与组装
自从上一篇文章到现在已经有将近一个月的时间,我将精力放在了前端页面分解与组装,和angularjs如何与jquery.bootstrap.D3等一系列其他类库结合使用的经验总结上.由于公司新招了一些员 ...
- angularJs项目实战!01:模块划分和目录组织
近日来我有幸主导了一个典型的web app开发.该项目从产品层次来说是个典型的CRUD应用,故而我毫不犹豫地采用了grunt + boilerplate + angularjs + bootstrap ...
- angularJs项目实战!03:angularjs与其他类库的协作(转)
angularjs,在我看来是个中等重量级的框架.即不像backbone那么简单,也不像dojo和Yui那么包罗万象.很多时候,妄图包罗万象,往往会出现很多子模块的质量高不成低不就,并且修改起来较为困 ...
- Java高级项目实战03:CRM系统数据库设计
接上一篇:Java高级项目实战02:客户关系管理系统CRM系统模块分析与介绍 欢迎点击回顾,接下来我们说说 CRM系统数据库设计. 我们根据产品的原型搞以及UI组的设计稿, 接下来就要设计数据库, 一 ...
- angularJs项目实战!04:angularjs的性能问题
上一篇文章中我花了很多口舌去介绍angularjs是一个中型框架,面对大型应用时少不了第三方类库的配合.而我的核心议题是:如何以angularjs的思路使用其他类库,这里jquery是最好的例子了,谁 ...
- 项目实战03:Keepalived 实现高可用
目录 实验一:实现keepalived主从方式高可用基于LVS-DR模式的应用实战: 1.环境准备: 2.在lvs-server-master 主上 3.在lvs-server-backup 从上 4 ...
- JAVAEE——SSH项目实战03:新增客户、数据字典、文件上传和修改客户
作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7145599.html 一.新增客户 1.数据字典 用于枚举项目中有限个数的字典项 (1 ...
- 【SSH网上商城项目实战03】使用EasyUI搭建后台页面框架
转自:https://blog.csdn.net/eson_15/article/details/51312490 前面两节,我们整合了SSH并且抽取了service和action部分的接口,可以说基 ...
- 【Robot Framework 项目实战 03】使用脚本自动生成统一格式的RF自动化用例
背景 虽然大家都已经使用了统一的关键字,但是在检查了一些测试用例之后,还是发现因为大家对RF的熟悉程度不一导致的测试用例颗粒度差异很大的情况:而且在手动方式转化测试用例过程中,有不少工作是完全重复的且 ...
随机推荐
- Mysql时间戳开始时间1970-01-01 00:00:00和PHP date慢8小时
mysql> select unix_timestamp('1970-01-01 00:00:01');+---------------------------------------+| un ...
- head,tail,cat,more,less
tail FILE -n 4,查看文件最后4行内容head FILE -n 10,查看文件最前4行内容 使用cat more less都可以查看文本内容,但是它们三者有什么区别呢?more和less的 ...
- android中操作SQLite注意事项
调用rawQuery()后,需要调用Cursor的movToFirst(); public Cursor query(String sql, String[] args){ SQLiteDatabas ...
- 【转】被误解的MVC和被神化的MVVM
被误解的MVC和被神化的MVVM 作者 唐巧 发布于 2015年11月2日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办 被误解的 MVC MVC 的历史 MV ...
- iOS UIView指定显示摸一个角弧形显示
关键代码: UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect: view.bounds byRoundingCorner ...
- uvalive 4851 Restaurant(扫描法)
题意:有一个M*M的网格,坐标[0...M-1,0...M-1] 网格里面有两个y坐标同样的宾馆A和B.以及n个餐厅,宾馆AB里面各有一个餐厅,编号1,2,其它餐厅编号3-n.如今你打算新开一家餐厅. ...
- 【Android】Activity的菜单机制和方法解析
Activity有一套机制来实现对菜单的管理,方法如下: 1. 初始化菜单 public boolean onCreateOptionsMenu(Menu menu) 此方法用于初始化菜单,其中men ...
- How To Make a Music Visualizer in iOS
Xinrong Guo on June 4, 2013 Tweet Learn how to create your own music visualizer! In the mid-seventi ...
- Android 字体设置
Android 对中文字体支持很不好~~ 需要加入相应的字体库 (1)创建布局Layout //创建线性布局 LinearLayout linearLayout=newLinearLayout(thi ...
- Android 基于Netty的消息推送方案之Hello World(一)
消息推送方案(轮询.长连接) 轮询 轮询:比较简单的,最容易理解和实现的就是客户端去服务器上拉信息,信息的及时性要求越高则拉信息的频率越高.客户端拉信息的触发可以是一些事件,也可以是一个定时器,不断地 ...