Home>

You need a control for entering minutes and seconds in a project,However, after investigating some open source projects, no suitable control was found.There is a similar control timepicker in angular bootstrap ui, but it does not drill down to the accuracy of minutes and 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 the directive:

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 link function above, ctrls is an array:ctrls [0] is the controller instance defined on this directive, and ctrls [1] is ngmodelctrl, which is the controller instance corresponding to ng-model. This order is actually defined by require:["minutesecondpicker", "?^ ngmodel"].

noteThe first dependency is the name of the directive itself,At this time, the corresponding instance declared by the controller in the directive will be passed in.The second dependency is written a bit strangely:"?^ ngmodel", "?means that even if the dependency is not foundDon't throw exceptions,That is, the dependency is optional.The meaning of ^ is to find the controller of the parent element.

Then, define some default settings used in this directive,Through constant directive:

app.constant ("minutesecondpickerconfig", {
 minutestep:1, secondstep:1, readonlyinput:false, mousewheel:true
});

This is followed by the controller corresponding to the directive. Its declaration is as follows:

app.controller ("minutesecondpickercontroller", ["$scope", "$attrs", "$parse", "minutesecondpickerconfig", function ($scope, $attrs, $parse, minutesecondpickerconfig) {
 ...
}]);

In the direct link function, the controller's init method 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, which is passed in the link function:element.find ("input"). So the first input box is for minutes.The second input box is used to enter seconds.

Then, check if the mousewheel property is overridden. If not, use the default mousewheel set in constant and make 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 ();
 });
 };

The init method finally sets some settings on the inputs themselves:

//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 the input illegal. It will expose a validity=false attribute in the scope to give the page a chance to react appropriately.

If the user uses a variable to represent minutestep or secondstep, then they 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:

<table>
 <tbody>
 <tr>
  <td>
  <a ng-click="incrementminutes ()">
   <span></span>
  </a>
  </td>
  <td></td>
  <td>
  <a ng-click="incrementseconds ()">
   <span></span>
  </a>
  </td>
  <td></td>
 </tr>
 <tr>
  <td ng-class="{" has-error ":invalidminutes}">
  <Input type="text" ng-model="minutes" ng-change="updateminutes ()" ng-mousewheel="incrementminutes ()" ng-readonly="readonlyinput" maxlength="3">
  </td>
  <td&:;</td>
  <td ng-class="{" has-error ":invalidseconds}">
  <Input type="text" ng-model="seconds" ng-change="updateseconds ()" ng-mousewheel="incrementseconds ()" ng-readonly="readonlyinput" maxlength="2">
  <td>
  <!-Preview column->
  <td>
  <span ng-show="validity">
   {{previewtime (minutes, seconds)}}
  </span>
  </td>
 </tr>
 <tr>
  <td>
  <a ng-click="decrementminutes ()">
   <span></span>
  </a>
  </td>
  <td></td>
  <td>
  <a ng-click="decrementseconds ()">
   <span></span>
  </a>
  </td>
  <td></td>
 </tr>
 </tbody>
</table>

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

<div>
 <h3>highlight on<span>{{moviename}}</span></h3>
</div>
<div>
 <div>
 <div>
  <h4>start time:</h4>
  <minute-second-picker ng-model="starttime" validity="starttimevalidity"></minute-second-picker>
 </div>
 <div>
  <h4>end time:</h4>
  <minute-second-picker ng-model="endtime" validity="endtimevalidity"<&/minute-second-picker>
 </div>
 </div>
 <div>
 <div>
  tags:
 </div>
 <div>
  <tags model="tags" src="s as s.name for s in sourcetags" options="{addable:" true "}"></tags>
 </div>
 </div>
</div>
<div>
 <button ng-click="ok ()" ng-disabled="! Starttimevalidity ||! Endtimevalidity || durationincorrect (endtime, starttime)">ok</button>
 <button ng-click="cancel ()">cancel</button>
</div>

If i want to learn more,Can clickHereTo learn,Attached 3 exciting topics for everyone:

  • Previous Mysql56 forgot the root password to modify the root password method
  • Next Python Drawing Tutorial
  • Trends