Knockoutjs.com - Contacts Editor

http://knockoutjs.com/examples/contactsEditor.html

by photo_tom

HTML

<script src="http://knockoutjs.com/js/jquery.tmpl.js"></script>
<script src="http://knockoutjs.com/js/knockout-1.2.1.js"></script>
<div class='liveExample'> 
    
    <h2>Contacts</h2> 
    <div data-bind='template: "contactsListTemplate"' id='contactsList'> </div> 
    <script id='contactsListTemplate' type='text/html'> 
        <table class='contactsEditor'>
            <tr>
                <th>First name</th>
                <th>Last name</th>
                <th>Phone numbers</th>
            </tr>
            {{each(i, contact) contacts()}}                    
            <tr>
                <td>
                    <input data-bind='value: firstName' />
                    <div><a href='#' data-bind='click: function() { viewModel.removeContact(contact) }'>Delete</a></div>
                </td>
                <td><input data-bind='value: lastName' /></td>
                <td>
                    <table>
                        {{each(i, phone) phones}}
                        <tr>
                            <td><input data-bind='value: type' /></td>
                            <td><input data-bind='value: number' /></td>
                            <td><a href='#' data-bind='click: function() { viewModel.removePhone(contact, phone) }'>Delete</a></td>
                        </tr>
                        {{/each}}
                    </table>
                    <a href='#' data-bind='click: function() { viewModel.addPhone(contact) }'>Add number</a>
                </td>
            </tr>
            {{/each}}
        </table>
    </script> 
    
    <p> 
        <button data-bind='click: addContact'>Add a contact</button> 
        <button data-bind='click: save, enable: contacts().length > 0'>Save to JSON</button> 
    </p> 
    
    <textarea data-bind='value: lastSavedJson' rows='5' cols='60' disabled='disabled'> </textarea> 
    
</div>

CSS

body { font-family: arial; font-size: 14px; }

.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
.liveExample select[multiple] { width: 100%; height: 8em; }
.liveExample h2 { margin-top: 0.4em; font-weight: bold; font-size: 1.2em; }

.liveExample TR { vertical-align: top; }
.liveExample TABLE, .liveExample TD, .liveExample TH { padding: 0.2em; border-width: 0; margin: 0; }
.liveExample TD A { font-size: 0.8em; text-decoration: none; }
.liveExample table.contactsEditor > tbody > TR { border-bottom: 1px solid silver; }
.liveExample td input { width: 8em; }

li { list-style-type: disc; margin-left: 20px; }

JavaScript

var viewModel = {
    contacts: new ko.observableArray([
        {
        firstName: "Danny",
        lastName: "LaRusso",
        phones: [
            {
            type: "Mobile",
            number: "(555) 121-2121"},
        {
            type: "Home",
            number: "(555) 123-4567"}]},
    {
        firstName: "Sensei",
        lastName: "Miyagi",
        phones: [
            {
            type: "Mobile",
            number: "(555) 444-2222"},
        {
            type: "Home",
            number: "(555) 999-1212"}]}
    ]),
    addContact: function() {
        viewModel.contacts.push({
            firstName: "",
            lastName: "",
            phones: []
        });
    },
    removeContact: function(contact) {
        viewModel.contacts.remove(contact);
    },
    addPhone: function(contact) {
        contact.phones.push({
            type: "",
            number: ""
        });
        viewModel.contacts.valueHasMutated();
    },
    removePhone: function(contact, phone) {
        ko.utils.arrayRemoveItem(contact.phones, phone);
        viewModel.contacts.valueHasMutated();
    },
    save: function() {
        viewModel.lastSavedJson(JSON.stringify(viewModel.contacts(), null, 2));
    },
    lastSavedJson: new ko.observable("")
};

ko.applyBindings(viewModel);