angularJS扩展指令集成部分jquery ui/plugin

最近一直研究angularjs,其双向绑定的确很强大,看中文社区,官方文档,参考github的ui示例,大概摸索下angularjs的开发方式——和传统的dom编程完全不同,jquery再锋利在angularjs的设计理念中,只能作为幕后者。AMD/CMD虽然能和angularjs一起使用,但angularjs自己的module设计,尤其是自动注入特性,比amd/cmd优越。和jquery plugin的扩展方式一样,amd/cmd期望以另一个非官方标准来积累js扩展,angularjs也是如此,开发者要作出选择,或组合或取舍。

 

言归正传:贴下一个简单以cmd形式扩展angularjs指令实现结合jquery/plugin的代码,需要的童鞋可以参考下,照此思路可以把jquery的有用的功能移植到angularjs的应用中来

 

// file ng.config.js
define(function(require){
	return {
		init: function(){
			// 默认配置
			var conf = {};

			conf.date = {dateFormat: 'yy-mm-dd'};
			conf.autocomplete = {minChars: 3, maxItemsToShow: 20};

			var md = angular.module('ng.config', []);
			md.value('ng.config', conf);
		}
	};
});

// file ng.filter.js
define(function(require){
	return {
		init: function(){
			var md = angular.module('ng.filter', []);

			// 过滤器方法都会执行两次。。。吐血
			// http://stackoverflow.com/questions/11676901/is-this-normal-for-angularjs-filtering

			// 日期格式转换
			md.filter('formatDate', function(){
				return function(value, format){
					if (!value)
						return value;

					return value.format(format || 'yyyy-MM-dd');
				};
			});
		}
	};
});

// file ng.service.js
define(function(require){
	return {
		init: function(){
			var md = angular.module('ng.service', []);

			// 注册服务
			// 全局变量获取,一种约定
			md.factory('uiGetPageData', ['$window', function(win){
				return function(key){
					var pageData = win.pageData;
					return pageData ? pageData[key] : null;
				};
			}]);

			// 日志
			md.factory('uiLog', ['$window', function(win){
				return function(msg, level){
					if(typeof(msg) != 'string')
						msg = JSON.stringify(msg);

					level = level || 'INFO';
					if(win.console && win.console.log)
						win.console.log('[' + level + ']' + msg);
				};
			}]);

			// 服务请求过滤
			md.factory('uiRequest', ['uiLog', function(log){
				return {
					filter: function(params, conf, skipLl){
						if(!conf)
							conf = {dateFormat: 'yyyy-MM-dd'};

						var r = {};
						if(params){
							for(key in params){
								if(skipLl && skipLl.contains(key))
									continue;

								var val = params[key];
								if(angular.isDate(val)){
									r[key] = val.format(conf.dateFormat);
								}else{
									r[key] = val;
								}
							}
						}
						return r;
					}
				};
			}]);

			// 验证服务
			md.factory('uiValid', function(){
				return {
					check: function(val, rule){
					}
				};
			});
		}
	};
});

// file ng.ui.js
define(function(require){
	require('ng/ng.config').init();
	require('ng/ng.service').init();
	require('ng/ng.filter').init();

	require('jquery.autocomplete');
	require('jquery.bgiframe');
	require('jquery.datepicker');
	require('jquery.hotkeys');

	var ag = window.angular;
	return {
		init: function(){
			var md = ag.module('ng.ui', ['ng.config', 'ng.service', 'ng.filter']);
			
			// 日期选择器
			// *** *** *** *** *** *** *** *** *** ***
			// *** *** *** *** *** *** *** *** *** ***
			md.directive('uiDate', ['ng.config', 'uiLog', function(conf, log){
				'use strict';
				var options = {};
				if(ag.isObject(conf.date)){
					ag.extend(options, conf.date);
				}
				return {
					restrict: 'A',
					require: 'ngModel',
					link: function(scope, el, attrs, ctrl){
						var getOptions = function(){
							return ag.extend(options, scope.$eval(attrs.uiDate));
						};

						var init = function(){
							var opts = getOptions();
							log('Init datepicker : ');
							log(opts);

							if(ctrl){
								// update model when datepicker value changes
								var updateModel = function(){
									scope.$apply(function(){
										var date = el.datepicker("getDate");
										ctrl.$setViewValue(date);
									});
								};

								if(opts.onSelect){
									var userHandler = opts.onSelect;
									opts.onSelect = function(value, picker){
										updateModel();
										return userHandler(value, picker);
									};
								}else{
									opts.onSelect = function(value, picker){
										updateModel();
									};
								}

								// datepicker后无法按键了
//								el.bind('change', updateModel);

								// Update the date picker when the model changes
								ctrl.$render = function(){
									var date = ctrl.$viewValue;
									if (ag.isDefined(date) && date !== null && !ag.isDate(date)){
										throw new Error('ng-Model value must be a Date object - currently it is a ' + typeof date + ' - use ui-date-format to convert it from a string');
									}

									el.datepicker("setDate", date);
								};
							}

							// If we don't destroy the old one it doesn't update properly when the config changes
							el.datepicker('destroy');
							// Create the new datepicker widget
							el.datepicker(opts);
							// Force a render to override whatever is in the input text box
							ctrl.$render();
						};

						// Watch for changes to the directives options
						scope.$watch(getOptions, init, true);
					}
				};
			}]);

			// 自动填充
			// *** *** *** *** *** *** *** *** *** ***
			// *** *** *** *** *** *** *** *** *** ***
			md.directive('uiAutocomplete', ['ng.config', 'uiLog', function(conf, log){
				'use strict';
				var options = {};
				if(ag.isObject(conf.autocomplete)){
					ag.extend(options, conf.autocomplete);
				}
				return {
					restrict: 'A',
					require: 'ngModel',
					link: function(scope, el, attrs, ctrl){
						var getOptions = function(){
							return ag.extend(options, scope.$eval(attrs.uiAutocomplete));
						};

						var init = function(){
							var opts = getOptions();
							log('Init autocomplete : ');
							log(opts);

							if(!opts.url || !opts.targetModel){
								log('Init autocomplete fail : url/targetModel required!');
								return;
							}

							if(ctrl){
								ctrl.$render = function(){
									// 自动填充如果没有填充的显示值,就默认取填充值
									var showLabel = ctrl.$viewValue;
									if(!showLabel){
										var targetModel = opts.targetModel;

										var showValue;
										if(targetModel.contains('.')){
											var arr = targetModel.split(/\./);
											var i = 0;
											var targetScope = scope;

											for(; i < arr.length; i++){
												var key = arr[i];
												if(i == arr.length - 1){
													showValue = targetScope[key];
												}else{
													if(!targetScope[key])
														break;
													targetScope = targetScope[key];
												}
											}
										}else{
											showValue = scope[targetModel];
										}
										if(showValue){
											ctrl.$setViewValue(showValue);
											el.val(showValue);
										}
									}
								};
							}

							// json : 
							// [{data: {result: i}, value: 'Col' + i}]
							el.autocomplete({
								url: opts.url, 
								minChars: opts.minChars, 
								maxItemsToShow: opts.maxItemsToShow, 
								remoteDataType: 'json', 
								useCache: false,
								processData: function(data) {
									var i, r = [];
									for(i = 0; i < data.length; i++){
										var item = data[i];
										r.push({data: {result: item.v}, value: item.v + '-' + item.l});
									}
									return r;
								},
								onItemSelect: function(item){
									var val = item.data.result;
									var showLabel = item.value;

									var targetModel = opts.targetModel;

									if(targetModel.contains('.')){
										var arr = targetModel.split(/\./);
										var i = 0;
										var targetScope = scope;

										for(; i < arr.length; i++){
											var key = arr[i];
											if(i == arr.length - 1){
												targetScope[key] = val;
											}else{
												if(!targetScope[key])
													targetScope[key] = {};
												targetScope = targetScope[key];
											}
										}
									}else{
										scope[targetModel] = val;
									}

									scope.$apply(function(){
										ctrl.$setViewValue(showLabel);
									});
								}
							});

							ctrl.$render();
						};

						// Watch for changes to the directives options
						scope.$watch(getOptions, init, true);
					}
				};
			}]);

			// 快捷键
			// *** *** *** *** *** *** *** *** *** ***
			// *** *** *** *** *** *** *** *** *** ***
			md.directive('uiShortkey', ['ng.config', 'uiLog', function(conf, log){
				'use strict';
				var options = {};
				return {
					restrict: 'A',
					link: function(scope, el, attrs, ctrl){
						var getOptions = function(){
							return ag.extend(options, scope.$eval(attrs.uiShortkey));
						};

						var init = function(){
							var opts = getOptions();
							log('Init shortkey : ');
							log(opts);

							if(!opts.key || !opts.method){
								log('Init shortkey fail : key/method required!');
								return;
							}

							$.hotkeys.add(opts.key, function(){
								var fn = scope[opts.method];
								if(fn)
									fn.call();
							});
						};

						// Watch for changes to the directives options
						scope.$watch(getOptions, init, true);
					}
				};
			}]);

			// 布局相关
			// *** *** *** *** *** *** *** *** *** ***
			// *** *** *** *** *** *** *** *** *** ***
			md.directive('uiLayoutCol', ['ng.config', 'uiLog', function(conf, log){
				'use strict';
				return {
					restrict: 'A',
					link: function(scope, el, attrs, ctrl){
						if('TR' != el[0].nodeName)
							return;

						log('Relayout...');

						var _tds = el.children('td');
						if(_tds.size() == 2){
							_tds.filter(':first').addClass('l');
							_tds.filter(':last').addClass('r');
						}else if(_tds.size() == 4){
							_tds.filter(':even').addClass('l2');
							_tds.filter(':odd').addClass('r2');
						}else if(_tds.size() == 6){
							_tds.eq(0).addClass('l3');
							_tds.eq(1).addClass('r3');
							_tds.eq(2).addClass('l3');
							_tds.eq(3).addClass('r3');
							_tds.eq(4).addClass('l3');
							_tds.eq(5).addClass('r3last');
						}

						// siblings tr set td text-align to right if exists label 
						el.siblings('tr').children('td').filter(function(){
							return $(this).find('label').size() > 0;
						}).addClass('ar');

						// set vertical-align = middle for label
			//			el.siblings('tr').children('td').each(function(){
			//				var _td = $(this);
			//				if(_td.find('label').size() > 0){
			//					_td.addClass('ar');
			//					_td.find('label').addClass('vm');
			//				}
			//			});
					}
				};
			}]);
		}
	};
});

 

 

 

你可能感兴趣的:(AngularJS)