Knockoutjs.com - Contacts Editor

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

HTML

<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<div>
    <div class="panel" data-bind="foreach: contacts">
        <div>
            <label data-bind="text: 'Visitor ' + $root.position($data)"></label>
        </div>
        <div>
            <!-- ## Panel Content -->
            <div>
                <label>First Name</label>
                <input type="text" data-bind='value: firstName,valueUpdate: "afterkeydown"' />
            </div>
            <div>
                <label>Last Name</label>
                <input type="text" data-bind='value: fathersLast,valueUpdate: "afterkeydown",' />
            </div>
                        <div>
                <label>Extra Name</label>
                <input type="text" data-bind='value: extraName,valueUpdate: "afterkeydown",' />
            </div>
            <div>
                <label>Country</label>
                <input type="text" data-bind='value: country,valueUpdate: "afterkeydown",' />
            </div>
            <button data-bind='click: $root.removeContact'>Delete</button>
            <button data-bind='click: $root.addContact, disable: isNotAddable'>Add New</button>
            <!-- ## / Panel Content -->
        </div>
    </div>
</div>

JavaScript

var initialData = [{
    firstName: "John",
    fathersLast: "Smith",
    country: "USA",
    extraName: "USA"
}, {
    firstName: "George",
    fathersLast: "Fox",
    country: "Canada",
    extraName: "USA"
}, {
    firstName: "George",
    fathersLast: "Fox",
    country: "Canada",
    extraName: "USA"
}];

var requiredParameters = [];

var Contact = function (contact, trigger) {
    var obj = {}

    for (var property in contact) {
        if (requiredParameters.indexOf(property) >= 0) {
            var obs = ko.observable(contact[property])
            obs.subscribe(function () {
                trigger.valueHasMutated();
            });            obj[property] = obs;
        } else {
            obj[property] = contact[property];
        }
    }

    obj.isNotAddable = ko.computed(function () {
        trigger();
        var addable = true;
        for (var paramIndex in requiredParameters) {
            var param = requiredParameters[paramIndex];

            var obsElement = this[param];
            var value = obsElement();

            if (value) {
                addable = addable && (value.length > 0);
            } else {
                return true;
            }
        }

        return !addable;
    }.bind(obj));
    return obj;
}

var ContactsModel = function (contacts) {
    var self = this;
    self.formElementTrigger = ko.observable();
    self.contacts = ko.observableArray(ko.utils.arrayMap(contacts, function (contact) {
        var obj = new Contact(contact, self.formElementTrigger);

        return obj;
    }));

    self.addContact = function () {
        self.contacts.push(new Contact({
            firstName: "",
            fathersLast: "",
            country: ""
        }, self.formElementTrigger));
    };

    self.position = function (contact) {
        return self.contacts.indexOf(contact);
    }

    self.removeContact = function (contact) {
        self.contacts.remove(contact);
    };
};

var model = new...