KO Validation & Twitter Bootstrap CSS

http://stackoverflow.com/questions/12222538/knockout-validation-and-twitter-bootstrap-error-css

by imbolc

HTML

<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script src="https://rawgithub.com/ericmbarnard/Knockout-Validation/master/Src/knockout.validation.js"></script>
<div data-bind="visible: selectedItem() == null">
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
    <th>First Name</th>
    <th>Last Name</th>
</tr>
</thead>
<tbody data-bind='foreach: items'>
    <tr>
        <td><span data-bind="text: firstName" /></td>     
        <td><span data-bind="text: lastName" /></td>     
    </tr>
</tbody>
</table>
<button data-bind="click: addItem">Add</button>
</div>

<div data-bind="with: selectedItem">
    <div class="control-group" data-bind="validationElement: firstName">
        <label class="control-label">First Name</label>
        <div class="controls">
            <input type="text" data-bind="value: firstName, valueUpdate: 'afterkeydown'"/>
         </div>
    </div>
    <div class="control-group" data-bind="validationElement: lastName">
        <label class="control-label">Last Name</label>
        <div class="controls">
            <input type="text" data-bind="value: lastName, valueUpdate: 'afterkeydown'"/>
        </div>
    </div>
    <button data-bind="click: $root.saveItem">Save</button>
    
    Number of errors:<span data-bind="text: errors().length"></span>
    
</div>

JavaScript

ko.validation.configure({
    decorateElement: true,
    errorElementClass: 'error'
});

var my = my || {};

my.data = [{
    firstName: "Jim",
    lastName: "Jimson"},
{
    firstName: "Bob",
    lastName: "Bobson"}]

my.Item = function() {
    var self = this;
    self.firstName = ko.observable().extend({
        required: true,
        minLength: 2,
        maxLength: 10
    });
    self.lastName = ko.observable().extend({
        required: true,
        minLength: 2,
        maxLength: 10
    });
    self.errors = ko.validation.group(self);
    return self;
};

my.viewModel = function() {
    var self = this,
        items = ko.observableArray(),
        loadItems = function() {
            $.each(my.data, function(i, el) {
                items.push(new my.Item().firstName(el.firstName).lastName(el.lastName));
            });
        },
        selectedItem = ko.observable(),
        addItem = function() {
            selectedItem(new my.Item());
        },
        editItem = function(item) {
            selectedItem(item);
        },
        saveItem = function() {
            if (selectedItem().errors().length != 0) {
                alert("Check your inputs!");   
                selectedItem().errors.showAllMessages();                
            }
            else {

                items.push(selectedItem());
                selectedItem(null);
            }
        }
        return {
            items: items,
            loadItems: loadItems,
            selectedItem: selectedItem,
            addItem: addItem,
            editItem: editItem,
            saveItem: saveItem
        };
}();

my.viewModel.loadItems();
ko.applyBindings(my.viewModel);