Creating time inputs (mm: ss: ms) in the AngularJS directive

I have a form where I need users to enter time, in the resource this is saved as minutes (or milliseconds, a simple change). I need a directive to get the second / millisecond value, then create 3 separate inputs for minutes, seconds and milliseconds.

Users can then modify each of the components, and the directive will then update the model with second / millisecond values ​​of the three components.

It seems I can take the model value, create 3 inputs and use moment.js to create the individual components of time.

Directive

angular.module('myExampleApp') .directive('lapTimeInput', function () { var tpl = '<div class="lap_time_input"> \ <input ng-model="lap_time.minutes" type="number" class="minutes" placeholder="00" min="0" max="15" step="1"> \ <span class="lap-time-sep">:</span> \ <input ng-model="lap_time.seconds" type="number" class="seconds" placeholder="00" min="0" max="59" step="1"> \ <span class="lap-time-sep">.</span> \ <input ng-model="lap_time.milliseconds" type="number" class="milliseconds" placeholder="000" min="0" max="999" step="1"> \ </div>'; return { restrict: 'A', template: tpl, replace: true, require: 'ngModel', scope: { }, link: function (scope, element, attrs, ngModel) { if (!ngModel) return; scope.$watch(function () { return ngModel.$modelValue; }, function(newValue) { // Using moment.js to extract min, sec & ms parts var duration = moment.duration(newValue, 'seconds'); scope.lap_time = { minutes: duration.minutes(), seconds: duration.seconds(), milliseconds: duration.milliseconds() } }); } }; }); 

controller

 $scope.lap = Lap.get({ id: 1 }); // $resource contains .lap_time property // OR $scope.lap = { lap_time: 90.999 } 

HTML

 <input type="text" lap-time-input ng-model="lap.lap_time" /> 

This plunker hopefully will be a little clearer

http://plnkr.co/edit/xmNtlItembSUFFZzaT9n?p=preview

Now I'm not even sure that I went the right way with $ watch on ngModel, I would not have guessed. As far as I understand, I need a directive to do 3 things:

  • Replace the element with three separate inputs for minutes, seconds, and milliseconds.
  • Show model value to user in created inputs using created inputs
  • When the user changes any of the three inputs, he updates the model value by turning 3 separate values ​​for seconds / milliseconds.

Even just pushing in the right direction would be a big help

+5
source share
2 answers

This is what I managed to come up with. An inaccurate answer, but should help you on your way.

Template

The template has been updated and transmitted during the circle as an attribute that will be retrieved through the scope of the directive (see the section of the directive).

  <div lap-time-input="lap.lap_time"></div> 

controller

 angular.module('myExampleApp') .controller('myExampleCtrl', function ($scope) { // This would usually be a $resouce return via a serice $scope.lap = { lap_time: 90.999 }; // watch value of lap time change here when you update minute/second/millisecond $scope.$watch('lap.lap_time', function (newLapTime) { console.log('newLapTime', newLapTime); }); }); 

Directive

A second / millisecond input field has been added to the input field. You probably don't need this, but I put it there for a visual / debugging reason.

 angular.module('myExampleApp') .directive('lapTimeInput', function () { var tpl = '<div class="lap_time_input"> \ <input ng-model="lapTimeInput" type="number" placeholder="00.00"> \ <input ng-model="lap_time.minutes" type="number" class="minutes" placeholder="00" min="0" max="15" step="1"> \ <span class="lap-time-sep">:</span> \ <input ng-model="lap_time.seconds" type="number" class="seconds" placeholder="00" min="0" max="59" step="1"> \ <span class="lap-time-sep">.</span> \ <input ng-model="lap_time.milliseconds" type="number" class="milliseconds" placeholder="000" min="0" max="999" step="1"> \ </div>'; return { restrict: 'A', template: tpl, replace: true, scope: { lapTimeInput: '=' }, link: function (scope) { // watch for changes in lapTimeInput and update the lap_time model/object scope.$watch('lapTimeInput', function (newValue) { var duration = moment.duration(newValue, 'seconds'); scope.lap_time = { minutes: duration.minutes(), seconds: duration.seconds(), milliseconds: duration.milliseconds() } }); // watch for changes in the lap_time model/object scope.$watchCollection('lap_time', function (newTime, oldTime) { console.log(newTime); // convert back to lap time with momentjs here scope.lapTimeInput = moment.duration(newTime, 'seconds').asSeconds(); }); } }; }); 

JSFIDDLE

+1
source

Well, I managed to do this with ngModel without adding any code to the controller, since I will need to use this directive in several places. I was also able to do validation markup.

 angular.module('exampleApp') .directive('lapTimeInput', function () { var tpl = '<div class="lap-time-input"> \ <div class="input-group"> \ <span class="input-group-addon"><label for="laps">Lap Time</label></span> \ <input ng-model="lap_time.minutes" type="number" class="form-control minutes" name="minutes" placeholder="00" min="0" max="15" step="1"> \ </div> \ <span class="form-control lap-time-sep">:</span> \ <input ng-model="lap_time.seconds" type="number" class="form-control seconds" placeholder="00" min="0" max="59" step="1"> \ <span class="form-control lap-time-sep">.</span> \ <input ng-model="lap_time.milliseconds" type="number" class="form-control milliseconds" placeholder="000" min="0" max="999" step="1"> \ </div>'; return { restrict: 'A', template: tpl, replace: true, require: 'ngModel', link: function (scope, element, attrs, ngModel) { if (!ngModel) return; ngModel.$formatters.unshift(function(modelValue) { if(!modelValue) return; var duration = moment.duration(parseInt(modelValue)); return { minutes: duration.minutes(), seconds: duration.seconds(), milliseconds: duration.milliseconds() }; }); ngModel.$render = function() { scope.lap_time = ngModel.$viewValue }; ngModel.$parsers.unshift(function(modelVal) { var duration = moment.duration(scope.lap_time); return duration.asMilliseconds(); }); ngModel.$validators.check = function(modelValue, viewValue) { if(viewValue) { if(viewValue.minutes === undefined || viewValue.minutes > 15) { return false; } if(viewValue.seconds === undefined || viewValue.seconds > 59) { return false; } if(viewValue.milliseconds === undefined || viewValue.milliseconds > 999) { return false; } } return true; }; scope.$watchCollection('lap_time', function(newVal) { ngModel.$setViewValue(newVal); }); } }; }); 

If I have something wrong, I welcome feedback, of course.

+1
source

All Articles