扩展 angular ui-bootstrap timepicker 支持秒功能

ui-bootstrap timepicker 只支持选择时分,但由于公司需要选择到秒, 可是又没有找到angular 相关的插件。索性就在 ui-bootstrap timepicker  的基础上扩展了一个。

 

  1. 首先创建我们的 template.js 文件 ,这个文件就是存放我们扩展的模板。 代码如下
     1 angular.module('myTemplate', ['template/timepicker/mytimepicker.html']);
     2 
     3 angular.module("template/timepicker/mytimepicker.html", []).run(["$templateCache", function ($templateCache) {
     4     $templateCache.put("template/timepicker/mytimepicker.html",
     5     "<table>\n" +
     6     "  <tbody>\n" +
     7     "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
     8     "      <td><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
     9     "      <td>&nbsp;</td>\n" +
    10     "      <td><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
    11     "       <td>&nbsp;</td>\n" +
    12     "      <td><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
    13     "      <td ng-show=\"showMeridian\"></td>\n" +
    14     "    </tr>\n" +
    15     "    <tr>\n" +
    16     "      <td class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
    17     "        <input style=\"width:50px;\" type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\">\n" +
    18     "      </td>\n" +
    19     "      <td>:</td>\n" +
    20     "      <td class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
    21     "        <input style=\"width:50px;\" type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\">\n" +
    22     "      </td>\n" +
    23      "      <td>:</td>\n" +
    24     "      <td class=\"form-group\" ng-class=\"{'has-error': invalidSeconds}\">\n" +
    25     "        <input style=\"width:50px;\" type=\"text\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\">\n" +
    26     "      </td>\n" +
    27     "      <td ng-show=\"showMeridian\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" +
    28     "    </tr>\n" +
    29     "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
    30     "      <td><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
    31     "      <td>&nbsp;</td>\n" +
    32     "      <td><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
    33      "      <td>&nbsp;</td>\n" +
    34     "      <td><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
    35     "      <td ng-show=\"showMeridian\"></td>\n" +
    36     "    </tr>\n" +
    37     "  </tbody>\n" +
    38     "</table>\n" +
    39     "");
    40 }]);
    View Code

     

  2. 创建我们的 directive.js 文件,这个文件就是存放我们扩展的指令。代码如下
      1 angular.module("myDirective", [])
      2     .controller('MyTimepickerController', ['$scope', '$element', '$attrs', '$controller', '$log', 'uibTimepickerConfig', '$locale', '$parse', function ($scope, $element, $attrs, $controller, $log, timepickerConfig, $locale, $parse) {
      3         var selected = new Date(),
      4       ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
      5       meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
      6 
      7         $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
      8         $element.removeAttr('tabindex');
      9 
     10         this.init = function (ngModelCtrl_, inputs) {
     11             ngModelCtrl = ngModelCtrl_;
     12             ngModelCtrl.$render = this.render;
     13 
     14             ngModelCtrl.$formatters.unshift(function (modelValue) {
     15                 return modelValue ? new Date(modelValue) : null;
     16             });
     17 
     18             var hoursInputEl = inputs.eq(0),
     19                 minutesInputEl = inputs.eq(1),
     20                 secondsInputEl = inputs.eq(2);
     21 
     22             var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
     23             if (mousewheel) {
     24                 this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
     25             }
     26 
     27             var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
     28             if (arrowkeys) {
     29                 this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
     30             }
     31 
     32             $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
     33             this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
     34         };
     35 
     36         var hourStep = timepickerConfig.hourStep;
     37         if ($attrs.hourStep) {
     38             $scope.$parent.$watch($parse($attrs.hourStep), function (value) {
     39                 hourStep = parseInt(value, 10);
     40             });
     41         }
     42 
     43         var minuteStep = timepickerConfig.minuteStep;
     44         if ($attrs.minuteStep) {
     45             $scope.$parent.$watch($parse($attrs.minuteStep), function (value) {
     46                 minuteStep = parseInt(value, 10);
     47             });
     48         }
     49         
     50         var secondsStep = 1;
     51         if ($attrs.secondsStep) {
     52             $scope.$parent.$watch($parse($attrs.secondsStep), function (value) {
     53                 secondsStep = parseInt(value, 10);
     54             });
     55         }
     56         var min;
     57         $scope.$parent.$watch($parse($attrs.min), function (value) {
     58             var dt = new Date(value);
     59             min = isNaN(dt) ? undefined : dt;
     60         });
     61 
     62         var max;
     63         $scope.$parent.$watch($parse($attrs.max), function (value) {
     64             var dt = new Date(value);
     65             max = isNaN(dt) ? undefined : dt;
     66         });
     67 
     68         $scope.noIncrementHours = function () {
     69             var incrementedSelected = addMinutes(selected, hourStep * 60);
     70             return incrementedSelected > max ||
     71               (incrementedSelected < selected && incrementedSelected < min);
     72         };
     73 
     74         $scope.noDecrementHours = function () {
     75             var decrementedSelected = addMinutes(selected, -hourStep * 60);
     76             return decrementedSelected < min ||
     77               (decrementedSelected > selected && decrementedSelected > max);
     78         };
     79 
     80         $scope.noIncrementMinutes = function () {
     81             var incrementedSelected = addMinutes(selected, minuteStep);
     82             return incrementedSelected > max ||
     83               (incrementedSelected < selected && incrementedSelected < min);
     84         };
     85 
     86         $scope.noDecrementMinutes = function () {
     87             var decrementedSelected = addMinutes(selected, -minuteStep);
     88             return decrementedSelected < min ||
     89               (decrementedSelected > selected && decrementedSelected > max);
     90         };
     91 
     92         $scope.noIncrementSeconds = function () {
     93             var incrementedSelected = addMinutes(selected, secondsStep);
     94             return incrementedSelected > max ||
     95               (incrementedSelected < selected && incrementedSelected < min);
     96         };
     97 
     98         $scope.noDecrementSeconds = function () {
     99             var decrementedSelected = addMinutes(selected, -secondsStep);
    100             return decrementedSelected < min ||
    101               (decrementedSelected > selected && decrementedSelected > max);
    102         };
    103 
    104         $scope.noToggleMeridian = function () {
    105             if (selected.getHours() < 13) {
    106                 return addMinutes(selected, 12 * 60) > max;
    107             } else {
    108                 return addMinutes(selected, -12 * 60) < min;
    109             }
    110         };
    111 
    112         // 12H / 24H mode
    113         $scope.showMeridian = timepickerConfig.showMeridian;
    114         if ($attrs.showMeridian) {
    115             $scope.$parent.$watch($parse($attrs.showMeridian), function (value) {
    116                 $scope.showMeridian = !!value;
    117 
    118                 if (ngModelCtrl.$error.time) {
    119                     // Evaluate from template
    120                     var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
    121                     if (angular.isDefined(hours) && angular.isDefined(minutes)) {
    122                         selected.setHours(hours);
    123                         refresh();
    124                     }
    125                 } else {
    126                     updateTemplate();
    127                 }
    128             });
    129         }
    130 
    131         // Get $scope.hours in 24H mode if valid
    132         function getHoursFromTemplate() {
    133             var hours = parseInt($scope.hours, 10);
    134             var valid = $scope.showMeridian ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
    135             if (!valid) {
    136                 return undefined;
    137             }
    138 
    139             if ($scope.showMeridian) {
    140                 if (hours === 12) {
    141                     hours = 0;
    142                 }
    143                 if ($scope.meridian === meridians[1]) {
    144                     hours = hours + 12;
    145                 }
    146             }
    147             return hours;
    148         }
    149 
    150         function getMinutesFromTemplate() {
    151             var minutes = parseInt($scope.minutes, 10);
    152             return (minutes >= 0 && minutes < 60) ? minutes : undefined;
    153         }
    154 
    155         function getSecondsFromTemplate() {
    156             var seconds = parseInt($scope.seconds, 10);
    157             return (seconds >= 0 && seconds < 60) ? seconds : undefined;
    158         }
    159 
    160         function pad(value) {
    161             return (angular.isDefined(value) && value.toString().length < 2) ? '0' + value : value.toString();
    162         }
    163 
    164         // Respond on mousewheel spin
    165         this.setupMousewheelEvents = function (hoursInputEl, minutesInputEl, secondsInputEl) {
    166             var isScrollingUp = function (e) {
    167                 if (e.originalEvent) {
    168                     e = e.originalEvent;
    169                 }
    170                 //pick correct delta variable depending on event
    171                 var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
    172                 return (e.detail || delta > 0);
    173             };
    174 
    175             hoursInputEl.bind('mousewheel wheel', function (e) {
    176                 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
    177                 e.preventDefault();
    178             });
    179 
    180             minutesInputEl.bind('mousewheel wheel', function (e) {
    181                 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
    182                 e.preventDefault();
    183             });
    184 
    185             secondsInputEl.bind('mousewhel wheel', function(e) {
    186                 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
    187                 e.preventDefault();
    188             });
    189 
    190         };
    191 
    192         // Respond on up/down arrowkeys
    193         this.setupArrowkeyEvents = function (hoursInputEl, minutesInputEl, secondsInputEl) {
    194             hoursInputEl.bind('keydown', function (e) {
    195                 if (e.which === 38) { // up
    196                     e.preventDefault();
    197                     $scope.incrementHours();
    198                     $scope.$apply();
    199                 } else if (e.which === 40) { // down
    200                     e.preventDefault();
    201                     $scope.decrementHours();
    202                     $scope.$apply();
    203                 }
    204             });
    205 
    206             minutesInputEl.bind('keydown', function (e) {
    207                 if (e.which === 38) { // up
    208                     e.preventDefault();
    209                     $scope.incrementMinutes();
    210                     $scope.$apply();
    211                 } else if (e.which === 40) { // down
    212                     e.preventDefault();
    213                     $scope.decrementMinutes();
    214                     $scope.$apply();
    215                 }
    216             });
    217 
    218             secondsInputEl.bind('keydown', function(e) {
    219                 if (e.which === 38) { // up
    220                     e.preventDefault();
    221                     $scope.incrementSeconds();
    222                     $scope.$apply();
    223                 } else if (e.which === 40) { // down
    224                     e.preventDefault();
    225                     $scope.decrementSeconds();
    226                     $scope.$apply();
    227                 }
    228             });
    229         };
    230 
    231         this.setupInputEvents = function (hoursInputEl, minutesInputEl, secondsInputEl) {
    232             if ($scope.readonlyInput) {
    233                 $scope.updateHours = angular.noop;
    234                 $scope.updateMinutes = angular.noop;
    235                 $scope.updateSeconds = angular.noop;
    236                 return;
    237             }
    238 
    239             var invalidate = function (invalidHours, invalidMinutes, invalidSeconds) {
    240                 ngModelCtrl.$setViewValue(null);
    241                 ngModelCtrl.$setValidity('time', false);
    242                 if (angular.isDefined(invalidHours)) {
    243                     $scope.invalidHours = invalidHours;
    244                 }
    245                 if (angular.isDefined(invalidMinutes)) {
    246                     $scope.invalidMinutes = invalidMinutes;
    247                 }
    248                 if (angular.isDefined(invalidSeconds)) {
    249                     $scope.invalidSeconds = invalidSeconds;
    250                 }
    251             };
    252 
    253             $scope.updateHours = function () {
    254                 var hours = getHoursFromTemplate(),
    255                   minutes = getMinutesFromTemplate();
    256 
    257                 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
    258                     selected.setHours(hours);
    259                     if (selected < min || selected > max) {
    260                         invalidate(true);
    261                     } else {
    262                         refresh('h');
    263                     }
    264                 } else {
    265                     invalidate(true);
    266                 }
    267             };
    268 
    269             hoursInputEl.bind('blur', function (e) {
    270                 if (!$scope.invalidHours && $scope.hours < 10) {
    271                     $scope.$apply(function () {
    272                         $scope.hours = pad($scope.hours);
    273                     });
    274                 }
    275             });
    276 
    277             $scope.updateMinutes = function () {
    278                 var minutes = getMinutesFromTemplate(),
    279                   hours = getHoursFromTemplate();
    280 
    281                 if (angular.isDefined(minutes) && angular.isDefined(hours)) {
    282                     selected.setMinutes(minutes);
    283                     if (selected < min || selected > max) {
    284                         invalidate(undefined, true);
    285                     } else {
    286                         refresh('m');
    287                     }
    288                 } else {
    289                     invalidate(undefined, true);
    290                 }
    291             };
    292             
    293             minutesInputEl.bind('blur', function (e) {
    294                 if (!$scope.invalidMinutes && $scope.minutes < 10) {
    295                     $scope.$apply(function () {
    296                         $scope.minutes = pad($scope.minutes);
    297                     });
    298                 }
    299             });
    300 
    301             $scope.updateSeconds = function () {
    302                 var minutes = getMinutesFromTemplate(),
    303                   hours = getHoursFromTemplate(),
    304                     seconds = getSecondsFromTemplate();
    305 
    306                 if (angular.isDefined(minutes) && angular.isDefined(hours) && angular.isDefined(seconds)) {
    307                     selected.setSeconds(seconds);
    308                     if (selected < min || selected > max) {
    309                         invalidate(undefined, undefined,true);
    310                     } else {
    311                         refresh('s');
    312                     }
    313                 } else {
    314                     invalidate(undefined, undefined, true);
    315                 }
    316             };
    317 
    318             secondsInputEl.bind('blur', function (e) {
    319                 if (!$scope.invalidSeconds && $scope.seconds < 10) {
    320                     $scope.$apply(function () {
    321                         $scope.seconds = pad($scope.seconds);
    322                     });
    323                 }
    324             });
    325         };
    326 
    327         this.render = function () {
    328             var date = ngModelCtrl.$viewValue;
    329 
    330             if (isNaN(date)) {
    331                 ngModelCtrl.$setValidity('time', false);
    332                 $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
    333             } else {
    334                 if (date) {
    335                     selected = date;
    336                 }
    337 
    338                 if (selected < min || selected > max) {
    339                     ngModelCtrl.$setValidity('time', false);
    340                     $scope.invalidHours = true;
    341                     $scope.invalidMinutes = true;
    342                     $scope.invalidSeconds = true;
    343                 } else {
    344                     makeValid();
    345                 }
    346                 updateTemplate();
    347             }
    348         };
    349 
    350         // Call internally when we know that model is valid.
    351         function refresh(keyboardChange) {
    352             makeValid();
    353             ngModelCtrl.$setViewValue(new Date(selected));
    354             updateTemplate(keyboardChange);
    355         }
    356 
    357         function makeValid() {
    358             ngModelCtrl.$setValidity('time', true);
    359             $scope.invalidHours = false;
    360             $scope.invalidMinutes = false;
    361             $scope.invalidSeconds = false;
    362         }
    363 
    364         function updateTemplate(keyboardChange) {
    365             var hours = selected.getHours(), minutes = selected.getMinutes(), seconds = selected.getSeconds();
    366 
    367             if ($scope.showMeridian) {
    368                 hours = (hours === 0 || hours === 12) ? 12 : hours % 12; // Convert 24 to 12 hour system
    369             }
    370 
    371             $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
    372             if (keyboardChange !== 'm') {
    373                 $scope.minutes = pad(minutes);
    374             }
    375             if (keyboardChange !== 's') {
    376                 $scope.seconds = pad(seconds);
    377             }
    378             $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
    379         }
    380         
    381         function addMinutes(date, minutes) {
    382             var dt = new Date(date.getTime() + minutes * 60000);
    383             var newDate = new Date(date);
    384             newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
    385             return newDate;
    386         }
    387 
    388         function addSeconds(date, seconds) {
    389             var dt = new Date(date.getTime() + seconds*1000);
    390             var newDate = new Date(date);
    391             newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
    392             return newDate;
    393         }
    394 
    395         function addMinutesToSelected(minutes) {
    396             selected = addMinutes(selected, minutes);
    397             refresh();
    398         }
    399 
    400         function addSecondsToSelected(seconds) {
    401             selected = addSeconds(selected, seconds);
    402             refresh();
    403         }
    404 
    405         $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
    406           $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
    407 
    408         $scope.incrementHours = function () {
    409             if (!$scope.noIncrementHours()) {
    410                 addMinutesToSelected(hourStep * 60);
    411             }
    412         };
    413 
    414         $scope.decrementHours = function () {
    415             if (!$scope.noDecrementHours()) {
    416                 addMinutesToSelected(-hourStep * 60);
    417             }
    418         };
    419 
    420         $scope.incrementMinutes = function () {
    421             if (!$scope.noIncrementMinutes()) {
    422                 addMinutesToSelected(minuteStep);
    423             }
    424         };
    425 
    426         $scope.decrementMinutes = function () {
    427             if (!$scope.noDecrementMinutes()) {
    428                 addMinutesToSelected(-minuteStep);
    429             }
    430         };
    431 
    432         $scope.incrementSeconds = function () {
    433             if (!$scope.noIncrementSeconds()) {
    434                 addSecondsToSelected(secondsStep);
    435             }
    436         };
    437 
    438         $scope.decrementSeconds = function () {
    439             if (!$scope.noDecrementSeconds()) {
    440                 addSecondsToSelected(-secondsStep);
    441             }
    442         };
    443 
    444         
    445         $scope.toggleMeridian = function () {
    446             if (!$scope.noToggleMeridian()) {
    447                 addMinutesToSelected(12 * 60 * (selected.getHours() < 12 ? 1 : -1));
    448             }
    449         };
    450 
    451     }])
    452     .directive("myTimepicker", function () {
    453         return {
    454             restrict: 'EA',
    455             require: ['myTimepicker', '?^ngModel'],
    456             controller: 'MyTimepickerController',
    457             controllerAs: 'myTimepicker',
    458             replace: true,
    459             scope: {},
    460             templateUrl: function (element, attrs) {
    461                 return attrs.templateUrl || 'template/timepicker/mytimepicker.html';
    462             },
    463             link: function (scope, element, attrs, ctrls) {
    464                 var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
    465 
    466                 if (ngModelCtrl) {
    467                     timepickerCtrl.init(ngModelCtrl, element.find('input'));
    468                 }
    469             }
    470         };
    471     });
    View Code

     

  3. 支持原生的所有配置,使用方法如下
    <my-timepicker ng-model="我们的Model"></my-timepicker>

    这样活生生的秒就出现了。

    你可以上翻下翻,还可以手动输入。

posted @ 2016-01-02 20:15  janjon  阅读(1191)  评论(0编辑  收藏  举报