Tag Archives: javascript

Validate Time Entry with Javascript

I was cleaning up a web form that had a textbox for the user to enter a time value. The thing I don’t love about using a textbox to capture a time value is that there’s no validation. The user might enter a bad value and not realize it, and I’d rather let them know right away rather than displaying a message after they try to submit the form.

Surely there’s something we can do with javascript and regular expressions to create an intuitive experience for the user, right?

Format Checkin’ Regular Expression

The first thing we’re going to need is a regular expression that can be used to determine if an entry is valid or not. I decided to use a pair: one for standard time and one for military time.

function validateTime(time) {
    if (!time) {
        return false;
    }
    var military = /^\s*([01]?\d|2[0-3]):[0-5]\d\s*$/i;
    var standard = /^\s*(0?\d|1[0-2]):[0-5]\d(\s+(AM|PM))?\s*$/i;
    return time.match(military) || time.match(standard);
}

Make Red When Invalid

Now that we have a way to determine if an entry is valid, we need to decide how to give that feedback to the user. My first thought was to use the input control’s keyup event to check the value and make the text red if it doesn’t match.

<input type="text" class="warnIfInvalid" />
$(new function () {
    $('.warnIfInvalid').on('keyup', function () {
        $(this).css('color', 'black');
        if (!validateTime($(this).val())) {
            $(this).css('color', 'red');
        }
    });
});

Change to Default When Invalid

The color feedback is nice, but what if our field is a required value? If the user doesn’t enter anything, there is nothing to let them know they did something wrong. So, my second idea was to use the input control’s blur event to force a default value if the user enters a blank or invalid value.

<input type="text" class="required" value="12:00 AM" />
$(new function () {
    $('.required').on('blur', function () {
        if (!validateTime($(this).val())) {
            $(this).val('12:00 AM');
        }
    });
});

Do Both!

I didn’t like simply changing the user’s value to a default value without letting them know that I’m about to do that. For example, my regular expression won’t match a standard time that doesn’t have a space between the minutes and AM/PM. We can combine both techniques described above to give the user feedback as they type but change their bad input to a default if they enter something invalid. (Note that I manually trigger the keyup event after changing the invalid value to my default value.)

<input type="text" class="required warnIfInvalid" value="12:00 AM" />
$(new function () {
    $('.required').on('blur', function () {
        if (!validateTime($(this).val())) {
            $(this).val('12:00 AM');
            $(this).keyup();
        }
    });
    $('.warnIfInvalid').on('keyup', function () {
        $(this).css('color', 'black');
        if (!validateTime($(this).val())) {
            $(this).css('color', 'red');
        }
    });
});

Live example can be found here: http://jsfiddle.net/adamprescott/Q9b6d/

Advertisements

Databinding Collections with Knockout

I’ve written a few articles about databinding using Knockout (here, here, and here), but I haven’t touched on collections yet. Collections have proved to be a little more challening and require a bit more work, but they still follow the same pattern.

In this post, I’ll work through a short example to demonstrate how to do two-way databinding with collection objects.

Create Your View Models

I’ve found that getting a collection to bind properly requires multiple view models. The applications that I’ve written have required a main, page-level view model and separate models for each item in the collection.

Let’s say we want to write an page that will manage a classroom roster. The primary model will be called rosterViewModel, and we’ll create another model to represent students, studentViewModel. When the page is initialized, we’ll create a new instance of rosterViewModel and bind it to the page using Knockout.

var rosterViewModel = function() {
    var self = this;
    self.students = ko.observableArray();
};

var studentViewModel = function() {
    var self = this;
    self.firstName = ko.observable();
    self.lastName = ko.observable();
};

ko.applyBindings(new rosterViewModel());

Allow Items to be Added

Adding new items to the databound collection is as easy as creating a new instance of the sub-item’s view model and adding it to the collection. This should be done in an event handler of the parent model. So, in our example, we’ll add an addStudent event that will add a new instance of studentViewModel to the students collection.

var rosterViewModel = function() {
    var self = this;
    self.students = ko.observableArray();
    self.addStudent = function() {
        self.students.push(new studentViewModel());
    };
};

Allow Items to be Removed

Removing items from the collection works the same way as adding. When the event is invoked from the item to be removed, you simply remove it from the collection. We create another event, removeStudent, that does this.

var rosterViewModel = function() {
    var self = this;
    self.students = ko.observableArray();
    self.addStudent = function() {
        self.students.push(new studentViewModel());
    };
    self.removeStudent = function(student) {
        self.students.remove(student);
    };
};

Populating the Collection

Populating the collection might seem like a no-brainer. You’ve got some data, just pass it to Knockout and let it do its job, right? That actually works okay if you’re not modifying the data. The problem is that the source data isn’t rigged for Knockout with ko.observables and ko.observableArrays. The result is that data populates, but changes aren’t detected.

What I’ve done to get around this is to loop through the source collection, instantiate the appropriate view model object, and add it to the parent model’s collection. To make things easier, I modify the view model to accept the source data as an input and populate properties when it’s provided.

var rosterViewModel = function(data) {
    var self = this;
    self.students = ko.observableArray();
    self.addStudent = function() {
        self.students.push(new studentViewModel());
    };
    self.removeStudent = function(student) {
        self.students.remove(student);
    };
    
    if (data != null) {
        var s = data.students;
        for (var i in s) {
            self.students.push(new studentViewModel(s[i]));
        }
    }
};

var studentViewModel = function(data) {
    var self = this;
    self.firstName = ko.observable();
    self.lastName = ko.observable();
    
    if (data != null) {
        self.firstName(data.firstName);
        self.lastName(data.lastName);
    }
};

var sourceData = {
    students: [{ firstName: "Adam", lastName: "Prescott" }]
};

ko.applyBindings(new rosterViewModel(sourceData));

Retrieving the Collection

If you need to retrieve your collection for processing, I suggest doing the opposite of what was done to populate the collection: loop through the view model and extract the data. What you do with the data is up to you. You can package it into another object to be sent to another service or method, or format it for display elsewhere on the page. To demonstrate, I created a computed property that creates an HTML list of students entered.

self.roster = ko.computed(function() {
    var result = "Students:<br/>";
    var s = self.students();
    for (var i in s) {
        var f = s[i].firstName() ? s[i].firstName() : "&lt;unknown&gt;";
        var l = s[i].lastName() ? s[i].lastName() : "&lt;unknown&gt;";
        result += l + ", " + f + "<br/>";
    }
    return result;
});

You can see the complete working example here.

Real-time Validation with Knockout

Knockout gives you a slick way to do real-time validation on your data with its built-in extenders. At first glance, it may not seem like an entirely intuitive process, but it’s actually pretty clever. When you use an extender, you’re extending the property to have additional sub-properties and behaviors. Makes sense, right?

Let’s look at a simple example: validating a date value.

We begin by creating a simple view model and UI.

var viewModel = function () {
    var self = this;
    this.dateValue = ko.observable();
};
ko.applyBindings(new viewModel());
<div>
    <input data-bind="value: dateValue" />
</div>

Now we can create our extender. We introduce a new hasError property that we’ll bind to the UI and use to show & hide validation messages. There’s a validate function that contains the validation logic. The validate method is called to perform the initial validation, and subscribe is called so that validate will be called when the value changes. Note that we also add valueUpdate: ‘afterkeydown’ to the input data-binding. This allows the validation to perform after each keystroke.

var viewModel = function () {
    var self = this;
    this.dateValue = ko.observable().extend({
        validDate: true
    });
};

ko.extenders.validDate = function (target) {
    target.hasError = ko.observable();

    function validate(value) {
        var invalid = isNaN(Date.parse(value));
        target.hasError(invalid);
    }

    validate(target());
    target.subscribe(validate);
    return target;
};

ko.applyBindings(new viewModel());
<div>
    <input data-bind="value: dateValue, valueUpdate: 'afterkeydown'" />
    <span data-bind="visible: dateValue.hasError">Invalid!</span>
</div>

That’s it for the hard stuff. All that’s left is to figure out what should be shown or hidden when the value is invalid or valid. I chose to show an alert icon and added some CSS to make the input box red.

.invalid input {
    border: 1px solid red;
}
.ui-icon {
    display: inline-block;
    vertical-align: middle;
}
<div data-bind="css: { invalid: dateValue.hasError }">
    <input data-bind="value: dateValue, valueUpdate: 'afterkeydown'" />
    <span class="ui-icon ui-icon-alert" title="Invalid value" data-bind="visible: dateValue.hasError"></span>
</div>

Check out this Fiddle for the working example.

Two-Way Databinding with jQuery

I was working on a small data entry page and wanted to use jQuery to databind an object to my controls. I was expecting a simple command, like $(“#myControl”).databind(obj.Value), but was surprised to find that one did not exist. The Internet kept telling me to look elsewhere–mostly Knockout–but I was sure that this could be accomplished with jQuery alone.

It turns out that it is easy to do with jQuery, but it’s all manual. I created a function named databind and, for each control, I populate it with the desired property’s value and create an event handler to update the value when the control changes.

function databind() {
    var ctl;

    // a checkbox
    ctl = $("#uxMyCheckbox");
    ctl.attr('checked', model.BoolValue);
    ctl.change(function () { model.BoolValue = this.checked; });

    // a textbox
    ctl = $("#uxMyTextbox");
    ctl.val(model.TextValue);
    ctl.change(function () { model.TextValue = this.value; });
}

It’s not quite as elegant as I was hoping for, but it’ll do for now. I’ll probably check out Knockout for the future, though.