Knockout.js - Form and list demo

by nitronic

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/jquery.tmpl.js"></script>
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.js"></script>
<script src="http://knockoutjs.com/examples/resources/knockout.simpleGrid.js"></script>
<div id="booksCollection">
    <form data-bind="submit: addItem" id="newEntryForm">
        <div class="formArea">
            <h1>Add new Entry</h1>
            
            <div class="errorBox" data-bind="visible: currentBook.hasErrors">
                ERROR: <span data-bind="text: currentBook.errorMessage"/>
            </div>
            
            <div id="row">
              <div id="col">
                  Title
              </div>
              <div id="col">
                <input data-bind="value: currentBook.title" placeholder="Enter new book title"></input>     
              </div>
            </div>
            <div id="row">
              <div id="col">
                  Author
              </div>
              <div id="col">
                <input data-bind="value: currentBook.author" placeholder="Enter new author"></input>
              </div>
            </div>
            <div id="row">
              <div id="col">
                  Pages
              </div>
              <div id="col">
                <input data-bind="value: currentBook.pages" placeholder="Enter new pages count"></input>
              </div>
            </div>            
            
            <button type="submit">Add book</button>
            
        </div>
    </form>
            
    <div class="collectionArea">
        <h1>Books</h1>
        <div data-bind='simpleGrid: gridViewModel'></div>
        
        <button data-bind="click: clear">Clear books</button>
    </div>
</div>

CSS

body {
  margin: 0.5em;  
}

.formArea {
 display: table; 
 padding: 15px;
}

.errorBox {
 background-color: red;
  color: white;  
    padding: 10px;
    border: 1px solid white;
    margin: 15px 0px 15px 0px;
}

#booksCollection {
    background-color: #ddd;
}

#row {
    display: table-row;
    height: 30px;
}

#col {
    display: table-cell;
}

h1 {
  font-size: 130%;
  font-weight: bold;  
  margin-bottom: 0.5em;
  margin-top: 1em;
}

.ko-grid { margin-bottom: 1em; width: 25em; border: 1px solid #000; background-color:White; }
.ko-grid th { text-align:left; background-color: #51A2FF; color:White; }
.ko-grid td, th { padding: 0.4em; }
.ko-grid tr.odd { background-color: #DDD; }
.ko-grid-pageLinks { margin-bottom: 1em; }
.ko-grid-pageLinks a { padding: 0.5em; }
.ko-grid-pageLinks a.selected { background-color: #51A2FF; color: White; }

.collectionArea {
    padding: 15px;
}

JavaScript

var BookViewModel = function() {
    this.title = ko.observable('');
    this.author = ko.observable('');
    this.pages = ko.observable('');

    this.errorMessage = ko.observable('');
    
    this.hasErrors = ko.computed(function() { return (this.errorMessage() != ''); }, this);

    this.reset = function() {
        this.title('');
        this.author('');
        this.pages('');
        this.errorMessage('');
    };

    this.validate = function() {
        this.errorMessage('');

        if (this.title().length === 0 || this.author().length === 0 || this.pages().length === 0) {
            this.errorMessage('All fields are required');
        }
    };
};

var initialData = [{
    title: "Windows Presentation Foundation 4.5",
    author: "Norbert Eder",
    pages: 400}
];

var BooksViewModel = function(items) {
    this.items = ko.observableArray(items);

    this.currentBook = new BookViewModel();

    this.addItem = function(formData) {
        this.currentBook.validate();
        if (!this.currentBook.hasErrors()) {
            this.items.push({
                title: formData[0].value,
                author: formData[1].value,
                pages: formData[2].value
            });
            this.currentBook.reset();
        }
    };

    this.clear = function() {
        this.items.removeAll();
    };

    this.gridViewModel = new ko.simpleGrid.viewModel({
        data: this.items,
        columns: [
            {
            headerText: "Title",
            rowText: "title"},
        {
            headerText: "Author",
            rowText: "author"},
        {
            headerText: "Pages",
            rowText: "pages"}
        ],
        pageSize: 5
    });
};

var firstViewModel = new BooksViewModel(initialData);

ko.applyBindings(firstViewModel, $('booksCollection').get(0));