仿Angular Bootstrap TimePicker创建分钟数-秒数的输入控件
在一个项目中需要一个用来输入分钟数和秒数的控件,然而调查了一些开源项目后并未发现合适的控件。在Angular Bootstrap UI中有一个类似的控件TimePicker,但是它并没有深入到分钟和秒的精度。 因此,决定参考它的源码然后自己进行实现。 最终的效果如下: 首先是该directive的定义: if(ngModelCtrl) {
minuteSecondPickerCtrl.init(ngModelCtrl,element.find('input')); } } }; }); 在以上的link函数中,ctrls是一个数组: ctrls[0]是定义在本directive上的controller实例,ctrls[1]是ngModelCtrl,即ng-model对应的controller实例。这个顺序实际上是通过require: ['minuteSecondPicker','?^ngModel']定义的。 注意到第一个依赖就是directive本身的名字,此时会将该directive中controller声明的对应实例传入。第二个依赖的写法有些奇怪:"?^ngModel",?的含义是即使没有找到该依赖,也不要抛出异常,即该依赖是一个可选项。^的含义是查找父元素的controller。 然后,定义该directive中用到的一些默认设置,通过constant directive实现: 紧接着是directive对应的controller,它的声明如下: 在directive的link函数中,调用了此controller的init方法: var minutesInputEl = inputs.eq(0),secondsInputEl = inputs.eq(1);
var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? init方法接受的第二个参数是inputs,在link函数中传入的是:element.find('input')。 所以第一个输入框用来输入分钟,第二个输入框用来输入秒。 然后,检查是否覆盖了mousewheel属性,如果没有覆盖则使用在constant中设置的默认mousewheel,并进行相关设置如下: // pick correct delta variable depending on event
var delta = (e.wheelData) ? e.wheelData : -e.deltaY; return (e.detail || delta > 0); }; minutesInputEl.bind('mousewheel wheel',function(e) { secondsInputEl.bind('mousewheel wheel',function(e) { init方法最后会对inputs本身进行一些设置: var invalidate = function(invalidMinutes,invalidSeconds) {
ngModelCtrl.$setViewValue(null); ngModelCtrl.$setValidity('time',false); $scope.validity = false; if(angular.isDefined(invalidMinutes)) { $scope.invalidMinutes = invalidMinutes; } if(angular.isDefined(invalidSeconds)) { $scope.invalidSeconds = invalidSeconds; } }; $scope.updateMinutes = function() { if(angular.isDefined(minutes)) { minutesInputEl.bind('blur',function(e) { $scope.updateSeconds = function() { if(angular.isDefined(seconds)) { secondsInputEl.bind('blur',function(e) { 此方法中,声明了用于设置输入非法的invalidate函数,它会在scope中暴露一个validity = false属性让页面有机会做出合适的反应。 如果用户使用了一个变量来表示minuteStep或者secondStep,那么还需要设置相应的watchers: var secondStep = minuteSecondPickerConfig.secondStep;
if($attrs.secondStep) { $scope.parent.$watch($parse($attrs.secondStep),function(value) { secondStep = parseInt(value,10); }); } 完整的directive实现代码如下: app.directive('minuteSecondPicker',element.find('input'));
} } }; }); app.constant('minuteSecondPickerConfig',mousewheel: true app.controller('minuteSecondPickerController',minuteSecondPickerConfig) { var selected = { this.init = function(ngModelCtrl_,secondsInputEl); var minuteStep = minuteSecondPickerConfig.minuteStep; // respond on mousewheel spin // respond on direct input this.render = function() { // adjust the time for invalid value at first time var totalSeconds = time.minutes * 60 + time.seconds; if(time) { // call internally when the model is valid function makeValid() { function updateTemplate(keyboardChange) { $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes); function pad(value) { function getMinutesFromTemplate() { function getSecondsFromTemplate() { return (seconds >= 0) ? seconds : undefined; $scope.incrementMinutes = function() { $scope.decrementMinutes = function() { $scope.incrementSeconds = function() { $scope.decrementSeconds = function() { function addSeconds(seconds) { selected = { refresh(); $scope.previewTime = function(minutes,seconds) { return hh + ':' + mm + ':' + ss; 对应的Template实现:
|
|
测试代码(即前面截图dialog的源代码):
<div id="highlight-start" class="col-xs-6">
Start Time:
<div id="highlight-end" class="col-xs-6">