AngularJS封装jQuery Datepicker

        在开始编码之前,我们必须想好组件的外观和运行方式。例如,我们想在HTML中像下面这样定义datepicker:

<input datepicker ng-model="currentDate" select="updateMyText(date)"></input>

        也就是说,我们需要修改Input输入标签,新增一个datepicker属性,然后 给它扩展更多功能(例如使用模型进行数据绑定,以及当选中了日期之后能够获得通知)。那么,我们应该如何实现呢?

        我们将会复用现有的控件,也就是jQuery UI提供的datepicker,而不是重新构建一个datepicker。我们只需要把它挂接到AngularJS上,然后启动它所提供的接口。

DatePickerDirective.js

angular.module('myApp.directives',[])
	.directive('datepicker', function() {
		return {
			//强制AngularJS把指令限定为只支持属性
			restrict: 'A',
			//总是和ng-model配合使用
			require:'?ngModel',
			scope: {
				//此方法需要与预先定义好,然后传递给视图控制器中的指令
				select: '&'	//把我们所引用的select函数绑定到右边的作用域中
			},
			link: function(scope, element, attrs, ngModel) {
				if(!ngModel) return;
				
				var optionsObj = {};
				
				optionsObj.dataFormat = 'mm/dd/yy';
				var updateModel = function(dateTxt) {
					scope.$apply(function() {
						//调用AngularJS内部的工具更新双向绑定关系
						ngModel.$setViewValue(dateTxt);
					});
				};
				
				optionsObj.onSelect = function(dateTxt, picker) {
					updateModel(dateTxt);
					if(scope.select) {
						scope.$apply(function() {
							scope.select({date: dateTxt});
						});
					}
				};
				
				ngModel.$render = function() {
					//使用AngularJS内部的'binding-specific'变量
					element.datepicker('setDate', ngModel.$viewValue || '');
				};
				element.datepicker(optionsObj);
			}
		};
	});

        我们还来看一看其中一些比较重要的东西。

        1.ng-model

        我们把一个ng-model属性传递给了指令所关联的函数。通过ng-model(它会托管指令的执行,因为所需要的属性都处于指令定义内部)可以定义,在指令的上下文中,连接到ng-model上的属性和对象应该具有何种行为。这里有两件事性需要注意:

        a.ngModel.$setViewValue(dateTxt)

        当AngularJS外部发生了某件事情的时候(在这个例子中是指jQuery UI datepicker的onSelect事件),需要调用此函数。这样就可以让AngularJS知道,它应该更新模型了。一般来说,当DOM事件发生的时候会调用它。

        b.ngModel.$render

        这是ng-model的另一个组成部分。它会告诉Angular,当模型发生变化的时候应该如何刷新视图。在这个例子中,我们只会把jQuery UI的datepicker变化之后的值传递过去。

        2.绑定select

        我们不会使用属性值进行编写、再根据作用域把它当做字符串来执行(在这种情况下,嵌套的函数和对象不可访问)的方式;而是会使用函数绑定技术(也就是“&”作用域绑定)。这样做在作用域上创建了一个新的函数,叫做select,它有一个参数——一个对象。这个对象中的每一个key都必须与HTML中所指定的参数一致(HTML就是使用指令的地方),key所对应的值将会被传递给函数对应的参数。这样带来的另一个优点是:控制器被解耦合了,它不再需要知道任何与DOM或者指令相关的内容。回调函数只需要执行它的动作,然后给出一些参数,而没有必要知道与数据绑定或刷新相关的内容。

        3.调用select

        注意,我们把一个optionOjb传递给了datepicker,同时也传递了一个onSelect函数。jQuery UI负责调用onSelect函数,它一般在AngularJS外部的执行上下文中进行调用。当然,在类似onSelect的函数被调用时,AngularJS是毫不知情的,所以我们有责任让AngularJS知道它需要进行何种操作。怎么才能做到这一点呢?使用scope.$apply即可。

        现在,我们可以在scope.$apply的外部简单地执行$setViewValue并调用scope.select,然后再调用scope.$apply()。但是,这样一来,在这两个步骤中任何一个里面所发生的异常都会被默默地忽略掉。如果异常发生在scope.$apply函数内部,则它们可以被AngularJS捕获到。

DatePickerController.js

var app = angular.module('myApp', ['myApp.directives']);

app.controller('MainCtrl', function($scope) {
	$scope.myText = 'Not Selected';
	$scope.currentDate = '';
	$scope.updateMyText = function(date) {
		$scope.myText = 'Selected';
	};
});

        以上代码声明了一个控制器,设置了一些作用域变量,然后创建了一个作用域方法(updateMyText),在后面我们会把这个方法绑定到datepicker的on-select事件上。

DatePickerHtml.html

<!DOCTYPE html>
<html ng-app="myApp">
	<head lang="en">
		<meta charset="utf-8"></meta>
		<title>AngularJS Datepicker</title>
		<script src="jquery/jquery-1.8.3.js"></script>
		<script src="jquery-ui/js/jquery-ui-1.9.2.js"></script>
		<script src="angular/angular.js"></script>
		<link rel="stylesheet" href="jquery-ui/css/custom-theme/jquery-ui-1.9.2.css"></link>
		<script src="directive/DatePickerDirective.js"></script>
		<script src="controller/DatePickerController.js"></script>
		<script src="jquery-ui/js/jquery.ui.datepicker-zh-CN.js" type="text/javascript" charset="gb2312"></script>
	</head>
	<body ng-controller="MainCtrl">
		<input id="dateField" datepicker ng-model="$parent.currentDate" select="updateMyText(date)"></input>
		<br></br>
		{{myText}} - {{currentDate}}
	</body>
</html>

        注意这里是如何指定select属性的。作用域中并没有"date"值,但是由于我们在指令中设置了函数绑定的方式,所以AngularJS知道这个函数将会接受一个名为"date"的参数,它就是当datepicker上的onSelect被调用时我们所指定的对象。

        注意一个细节:我们把ng-model设置成了$parent.currentDate,而不是currentDate。这是为什么呢?因为我们的指令将会创建一个独立的作用域,这样就可以用它来绑定select函数了。这就导致了currentDate不再被连接到ng-model上,即使我们专门进行了设置。因此,我们需要明确地告诉AngularJS,它需要引用的currentDate并不在这个独立的作用域中,而是在父作用域中。

        通过这种方式,当你把应用加载到浏览器之后,将会看到一个文本框,点击它就会打开一个jQuery UI的datepicker。选择日期之后,屏幕上的文本就会从"Not Selected"更新成"Selected",并加上你所选择的日期。同时,输入框中的日期也会被刷新。如下所示:

AngularJS封装jQuery Datepicker

AngularJS封装jQuery Datepicker

AngularJS封装jQuery Datepicker

资料来源:《用AngularJS开发下一代Web应用》

相关推荐