Angular.js

Time Input Widget with AngularJS

I have been working with the AngularJS framework for a couple of years now. One of the features I love the most about Angular is the powerful directive system that allows you to extend and create your own HTML vocabulary.

At one of my clients recently, there was a requirement for an HTML time input widget in which the user would interact primarily using keyboards (as opposed to click or tap). The client wanted something that requires as little as possible number of keystroke to enter a time in 12h format. The client liked how Chrome implements HTML5 time input, but unfortunately Chrome isn’t the browser of choice for the internal application.

So my task was to make something that would work across browsers and mimic that behavior. In this blog post, I will show you how I implemented it with Angular directive.

The HTML

If you were to set an input field to time type and open it in Chrome, you would see something like –:– — in the input box. It represents hours, minutes and AM/PM switch. So for our markup, we would need three regular text input boxes and a container that hosts all three of them.

The markup looks something like this:

<div class="png-time form-control">
    <input type="text" name="hour" class="hour" maxlength="2" />
    <span>:</span>
    <input type="text" name="minute" class="minute" maxlength="2 />
    <input type="text" name="mode" class="mode" maxlength="2" />
</div>

The CSS

The markup looks easy enough, but then it would look nothing like Chrome implementation. We need some CSS styling to make it look better.

I used the Bootstrap framework with some modifications. In addition, I need the following style to align these input boxes in the container:

.png-time.form-control {
  padding-left: 10px;
}

.png-time input {
  border: none;
  width: 17px;
  padding: 0;
  margin: 0;
  text-align: center;
  cursor: default;
}

.png-time input:focus {
  color: #000;
}

.png-time input.mode {
  width: 22px;
  margin-left: 3px;
}

The JavaScript

Here is where all the magic happens. I want to wrap the HTML above into a directive template so that it can be able to be reused anywhere. Something like the following:

<png-time-input model="main.time"></png-time-input>

Creating a directive in Angular is simple. Here is the code snippet that creates my custom png-time-input tag:

var TEMPLATE = '<div class="png-time form-control" ng-click="focus()">' +
    '<input type="text" name="hour" class="hour" maxlength="2" ng-model="hour" />' +
    '<span>:</span>' +
    '<input type="text" name="minute" class="minute" maxlength="2" ng-model="minute" />' +
    '<input type="text" name="mode" class="mode" maxlength="2" ng-model="mode"/>' +
    '</div>';

angular.module('png.timeinput', [])
    .directive('pngTimeInput', TimeInputDirective);

function TimeInputDirective() {
    return {
        restrict: 'E',
        replace: true,
        template: TEMPLATE,
        scope: {
            model: '='
        },
        link: timepickerLinkFn
    };

Now that you have your directive set up, it is time to manipulate the DOM elements and events of the widget. These happen inside the link function of the directive. There are few things to consider.

Handling character input
Since the input boxes are text boxes, they can take in any characters, and we do not want that. For the hour and minute input box, we want to limit to only numeric and within a certain range. Hour input would have range from 1 to 12 and minute input would have range from 0 to 59. AM/PM switcher is going to have just those 2 values.

For those tasks, I implemented a custom parser for each of the inputs and added them to the pipeline. Parsers get invoked when the DOM value is changed, so it is a perfect place to catch user input and manipulate them to our liking before committing the value to the model.

Handling user intention
In the parsers, I also make some guesses to see if a user meant to type certain numbers and move them on to the next field. For example, if user types 3 in the hour input, it most likely they want to put 03 and move on to minute field. Also in AM/PM switch, if they type ‘a’ it sets to AM, anything else it sets to PM.

Handling keyboard navigation
Users can also use arrow key or tab key to move between input boxes. I added event handlers in each of the input boxes to handle this.

Data binding
The model that binds to the widget is a JavaScript date object, which is represented by three input boxes. I need to set up a watch so that if any of the inputs change, I would update the widget model.

The Result

The widget turns out to be quite as close as Chrome implementation of time input. There are things like arrow up/down to change the value of the input boxes or second and millisecond values are missing, but that would be for future improvements.

Demo and source code

https://rawgit.com/phng/angular-input-time/master/index.html

https://github.com/phng/angular-input-time

Reference: Time Input Widget with AngularJS from our WCG partner Keyhole Software at the Keyhole Software blog.

Keyhole Software

Keyhole is a midwest-based consulting firm with a tight-knit technical team. We work primarily with Java, JavaScript and .NET technologies, specializing in application development. We love the challenge that comes in consulting and blog often regarding some of the technical situations and technologies we face.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button