[AngularJS] Create an input control (minuteSecondPicker) in minutes and seconds following Angular Bootstrap TimePicker)

Source: Internet
Author: User

[AngularJS] Create an input control (minuteSecondPicker) in minutes and seconds following Angular Bootstrap TimePicker)

In a project, a control is required to input minutes and seconds. However, some open-source projects have not found any suitable control. There is a similar control TimePicker in the Angular Bootstrap UI, but it is not accurate to minutes or seconds.

Therefore, I decided to refer to its source code and implement it myself.

The final effect is as follows:

The first is the definition of this ctictive:

app.directive('minuteSecondPicker', function() {    return {        restrict: 'EA',        require: ['minuteSecondPicker', '?^ngModel'],        controller: 'minuteSecondPickerController',        replace: true,        scope: {            validity: '='        },        templateUrl: 'partials/directives/minuteSecondPicker.html',        link: function(scope, element, attrs, ctrls) {            var minuteSecondPickerCtrl = ctrls[0],                ngModelCtrl = ctrls[1];            if(ngModelCtrl) {                minuteSecondPickerCtrl.init(ngModelCtrl, element.find('input'));            }        }    };});

In the above link function, ctrls is an array: ctrls [0] is the controller instance defined on this direve ve, ctrls [1] is ngModelCtrl, that is, the controller instance corresponding to the ng-model. This order is actually indicated by require: ['minutesecondpicker ','? ^ Ngmodel.

Note that the first dependency is the name of directive itself. At this time, the corresponding instance declared by the controller in the directive will be passed in. The second dependency is written in a strange way :"? ^ NgModel ",? Does not throw an exception even if the dependency is not found, that is, the dependency is optional. ^ Indicates finding the controller of the parent element.

Then, define some default settings used in this ve ctive and implement them through constant direve ve:

app.constant('minuteSecondPickerConfig', {    minuteStep: 1,    secondStep: 1,    readonlyInput: false,    mousewheel: true});

Then the controller corresponding to directive is declared as follows:

app.controller('minuteSecondPickerController', ['$scope', '$attrs', '$parse', 'minuteSecondPickerConfig',     function($scope, $attrs, $parse, minuteSecondPickerConfig) {    ...}]);

In the link function of directive, the init method of the controller is called:

   this.init = function(ngModelCtrl_, inputs) {        ngModelCtrl = ngModelCtrl_;        ngModelCtrl.$render = this.render;        var minutesInputEl = inputs.eq(0),            secondsInputEl = inputs.eq(1);        var mousewheel = angular.isDefined($attrs.mousewheel) ?             $scope.$parent.$eval($attrs.mousewheel) : minuteSecondPickerConfig.mousewheel;        if(mousewheel) {            this.setupMousewheelEvents(minutesInputEl, secondsInputEl);        }        $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ?            $scope.$parent.$eval($attrs.readonlyInput) : minuteSecondPickerConfig.readonlyInput;        this.setupInputEvents(minutesInputEl, secondsInputEl);    };

The second parameter accepted by the init method is inputs. In the link function, element. find ('input') is input '). Therefore, the first input box is used to input minutes, and the second input box is used to input seconds.

Then, check whether the mousewheel attribute is overwritten. If the attribute is not overwritten, use the default mousewheel set in constant and perform the following settings:

    // respond on mousewheel spin    this.setupMousewheelEvents = function(minutesInputEl, secondsInputEl) {        var isScrollingUp = function(e) {            if(e.originalEvent) {                e = e.originalEvent;            }            // 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) {            $scope.$apply((isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes());            e.preventDefault();        });        secondsInputEl.bind('mousewheel wheel', function(e) {            $scope.$apply((isScrollingUp(e)) ? $scope.incrementSeconds() : $scope.decrementSeconds());            e.preventDefault();        });    };

At last, the init method will set the inputs itself:

    // respond on direct input    this.setupInputEvents = function(minutesInputEl, secondsInputEl) {        if($scope.readonlyInput) {            $scope.updateMinutes = angular.noop;            $scope.updateSeconds = angular.noop;            return;        }        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() {            var minutes = getMinutesFromTemplate();            if(angular.isDefined(minutes)) {                selected.minutes = minutes;                refresh('m');            } else {                invalidate(true);            }        };        minutesInputEl.bind('blur', function(e) {            if(!$scope.invalidMinutes && $scope.minutes < 10) {                $scope.$apply(function() {                    $scope.minutes = pad($scope.minutes);                });            }        });        $scope.updateSeconds = function() {            var seconds = getSecondsFromTemplate();            if(angular.isDefined(seconds)) {                selected.seconds = seconds;                refresh('s');            } else {                invalidate(undefined, true);            }        };        secondsInputEl.bind('blur', function(e) {            if(!$scope.invalidSeconds && $scope.seconds < 10) {                $scope.$apply(function() {                    $scope.seconds = pad($scope.seconds);                });            }        });    };

In this method, the invalidate function is declared to set invalid input. It exposes a validity = false attribute in the scope to give the page a proper response.

If you use a variable to represent minuteStep or secondStep, you also need to set the corresponding watchers:

    var minuteStep = minuteSecondPickerConfig.minuteStep;    if($attrs.minuteStep) {        $scope.parent.$watch($parse($attrs.minuteStep), function(value) {            minuteStep = parseInt(value, 10);        });    }    var secondStep = minuteSecondPickerConfig.secondStep;    if($attrs.secondStep) {        $scope.parent.$watch($parse($attrs.secondStep), function(value) {            secondStep = parseInt(value, 10);        });    }

The complete directive implementation code is as follows:

var app = angular.module("minuteSecondPickerDemo");app.directive('minuteSecondPicker', function() {    return {        restrict: 'EA',        require: ['minuteSecondPicker', '?^ngModel'],        controller: 'minuteSecondPickerController',        replace: true,        scope: {            validity: '='        },        templateUrl: 'partials/directives/minuteSecondPicker.html',        link: function(scope, element, attrs, ctrls) {            var minuteSecondPickerCtrl = ctrls[0],                ngModelCtrl = ctrls[1];            if(ngModelCtrl) {                minuteSecondPickerCtrl.init(ngModelCtrl, element.find('input'));            }        }    };});app.constant('minuteSecondPickerConfig', {    minuteStep: 1,    secondStep: 1,    readonlyInput: false,    mousewheel: true});app.controller('minuteSecondPickerController', ['$scope', '$attrs', '$parse', 'minuteSecondPickerConfig',     function($scope, $attrs, $parse, minuteSecondPickerConfig) {    var selected = {            minutes: 0,            seconds: 0        },        ngModelCtrl = {            $setViewValue: angular.noop        };    this.init = function(ngModelCtrl_, inputs) {        ngModelCtrl = ngModelCtrl_;        ngModelCtrl.$render = this.render;        var minutesInputEl = inputs.eq(0),            secondsInputEl = inputs.eq(1);        var mousewheel = angular.isDefined($attrs.mousewheel) ?             $scope.$parent.$eval($attrs.mousewheel) : minuteSecondPickerConfig.mousewheel;        if(mousewheel) {            this.setupMousewheelEvents(minutesInputEl, secondsInputEl);        }        $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ?            $scope.$parent.$eval($attrs.readonlyInput) : minuteSecondPickerConfig.readonlyInput;        this.setupInputEvents(minutesInputEl, secondsInputEl);    };    var minuteStep = minuteSecondPickerConfig.minuteStep;    if($attrs.minuteStep) {        $scope.parent.$watch($parse($attrs.minuteStep), function(value) {            minuteStep = parseInt(value, 10);        });    }    var secondStep = minuteSecondPickerConfig.secondStep;    if($attrs.secondStep) {        $scope.parent.$watch($parse($attrs.secondStep), function(value) {            secondStep = parseInt(value, 10);        });    }    // respond on mousewheel spin    this.setupMousewheelEvents = function(minutesInputEl, secondsInputEl) {        var isScrollingUp = function(e) {            if(e.originalEvent) {                e = e.originalEvent;            }            // 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) {            $scope.$apply((isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes());            e.preventDefault();        });        secondsInputEl.bind('mousewheel wheel', function(e) {            $scope.$apply((isScrollingUp(e)) ? $scope.incrementSeconds() : $scope.decrementSeconds());            e.preventDefault();        });    };    // respond on direct input    this.setupInputEvents = function(minutesInputEl, secondsInputEl) {        if($scope.readonlyInput) {            $scope.updateMinutes = angular.noop;            $scope.updateSeconds = angular.noop;            return;        }        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() {            var minutes = getMinutesFromTemplate();            if(angular.isDefined(minutes)) {                selected.minutes = minutes;                refresh('m');            } else {                invalidate(true);            }        };        minutesInputEl.bind('blur', function(e) {            if(!$scope.invalidMinutes && $scope.minutes < 10) {                $scope.$apply(function() {                    $scope.minutes = pad($scope.minutes);                });            }        });        $scope.updateSeconds = function() {            var seconds = getSecondsFromTemplate();            if(angular.isDefined(seconds)) {                selected.seconds = seconds;                refresh('s');            } else {                invalidate(undefined, true);            }        };        secondsInputEl.bind('blur', function(e) {            if(!$scope.invalidSeconds && $scope.seconds < 10) {                $scope.$apply(function() {                    $scope.seconds = pad($scope.seconds);                });            }        });    };    this.render = function() {        var time = ngModelCtrl.$modelValue ? {            minutes: ngModelCtrl.$modelValue.minutes,            seconds: ngModelCtrl.$modelValue.seconds        } : null;        // adjust the time for invalid value at first time        if(time.minutes < 0) {            time.minutes = 0;        }        if(time.seconds < 0) {            time.seconds = 0;        }        var totalSeconds = time.minutes * 60 + time.seconds;        time = {            minutes: Math.floor(totalSeconds / 60),            seconds: totalSeconds % 60        };        if(time) {            selected = time;            makeValid();            updateTemplate();        }    };    // call internally when the model is valid    function refresh(keyboardChange) {        makeValid();        ngModelCtrl.$setViewValue({            minutes: selected.minutes,            seconds: selected.seconds        });        updateTemplate(keyboardChange);    }    function makeValid() {        ngModelCtrl.$setValidity('time', true);        $scope.validity = true;        $scope.invalidMinutes = false;        $scope.invalidSeconds = false;    }    function updateTemplate(keyboardChange) {        var minutes = selected.minutes,            seconds = selected.seconds;        $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);        $scope.seconds = keyboardChange === 's' ? seconds : pad(seconds);    }    function pad(value) {        return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;    }    function getMinutesFromTemplate() {        var minutes = parseInt($scope.minutes, 10);        return (minutes >= 0) ? minutes : undefined;    }    function getSecondsFromTemplate() {        var seconds = parseInt($scope.seconds, 10);        if(seconds >= 60) {            seconds = 59;        }        return (seconds >= 0) ? seconds : undefined;    }    $scope.incrementMinutes = function() {        addSeconds(minuteStep * 60);    };    $scope.decrementMinutes = function() {        addSeconds(-minuteStep * 60);    };    $scope.incrementSeconds = function() {        addSeconds(secondStep);    };    $scope.decrementSeconds = function() {        addSeconds(-secondStep);    };    function addSeconds(seconds) {        var newSeconds = selected.minutes * 60 + selected.seconds + seconds;        if(newSeconds < 0) {            newSeconds = 0;        }        selected = {            minutes: Math.floor(newSeconds / 60),            seconds: newSeconds % 60        };        refresh();    }    $scope.previewTime = function(minutes, seconds) {        var totalSeconds = parseInt(minutes, 10) * 60 + parseInt(seconds, 10),            hh = pad(Math.floor(totalSeconds / 3600)),            mm = pad(minutes % 60),            ss = pad(seconds);        return hh + ':' + mm + ':' + ss;    };}]);

Corresponding Template implementation:

 
 
&nbsp; &nbsp;
: {{ previewTime(minutes, seconds) }}
&nbsp; &nbsp;

Test code (the source code of the preceding dialog ):

    Highlight on {{ movieName }}                        Start Time:            
                             End Time:            
                                     Tags:                            
                 OK    Cancel

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.