Multiple form entries

Experimental UX for entering multiple entries of the same object. Uses Backbone and localStorage to persist data. Won't work in IE9-

by davidhong

HTML

<link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<script src="https://raw.github.com/janl/mustache.js/master/mustache.js"></script>
<script src="https://github.com/hongymagic/jQuery.serializeObject/raw/master/jQuery.serializeObject.js"></script>
<script src="http://documentcloud.github.com/backbone/examples/backbone-localstorage.js"></script>
<div id="pets" class="well">
    <header>
        <h1>Register your pets</h1>
        <caption>All form entries are automatically saved</caption>
    </header>
    <form type="post" class="form-stacked clearfix">
    </form>
    <div class="well">
        <button id="create" class="btn success">I have another pet</button>&nbsp;
        <button id="save" class="btn primary">I have entered all my pets, continue</button>
    </div>
</div>

<!-- 
    Mustache template representing each Pet
-->
<script type="html/template" id="pet">
    <fieldset id="pet-{{cid}}" class="alert-message block-message success">
        <div class="close">×</div>
        <h2>Pet #{{cid}}<h2>
        <div class="field clearfix">
            <input name="name[{{cid}}]" type="text" placeholder="Name of your pet" value="{{name}}" />
        </div>
        <div class="field clearfix">
            <input name="breed[{{cid}}]" type="text" placeholder="Breed of your pet" value="{{breed}}" />
        </div>
        <ul class="field input-lists">
            <li>
                <label for="type-cat[{{cid}}]"><input name="type[{{cid}}]" type="radio" id="type-cat[{{cid}}]" value="cat"><span>Cat</span></label>
            </li>
            <li>
                <label for="type-dog[{{cid}}]"><input name="type[{{cid}}]" type="radio" id="type-dog[{{cid}}]" value="dog"><span>Dog</span></label>
            </li>
        </ul>
    </fieldset>
</script>

CSS

img, article, aside, figure, footer, header, hgroup, menu, nav, section { display: block; }

#pets { margin: 1em auto; max-width: 640px; }

form { display: block; }
form .close { color: red; cursor: pointer; }
form ul { list-style-type: none; }

JavaScript

$(function() {
    // Pet model
    var Pet = Backbone.Model.extend({
        name: '',
        breed: '',
        type: ''
    });

    // Pets collection
    // This collection is persisted via localStorage
    var PetsList = Backbone.Collection.extend({
        model: Pet,
        localStorage: new Store("pets")
    });

    // Pet entry view
    PetEntryView = Backbone.View.extend({
        template: $('#pet').html(),
        initialize: function() {
            this.render();
        },
        render: function() {
            var model = this.model.toJSON();
            model.cid = this.model.cid;

            $(this.el).html(Mustache.to_html(this.template, model));
            return this;
        },
        events: {
            'click .close': 'close',
            'keyup input': 'save',
            'click input': 'save'
        },
        close: function() {
            if (confirm('Are you sure you wish to remove this pet?')) {

                this.model.destroy();
                $(this.el).remove();
            }
        },
        save: function() {
            var $el = $(this.el);
            this.model.save({
                name: $el.find('[name="name\[' + this.model.cid + '\]"]').val(),
                type: $el.find('[name="type\[' + this.model.cid + '\]"]:selected').val(),
                breed: $el.find('[name="breed\[' + this.model.cid + '\]"]').val()
            });
        }
    });

    // Pets entry list view
    PetsEntryView = Backbone.View.extend({
        el: $('#pets'),
        initialize: function() {
            Pets.bind('add', this.add, this);
            Pets.bind('reset', this.addAll, this);
            Pets.bind('all', this.render, this);

            // Fetch data from localStorage
            Pets.fetch();

            // If there are no pets, add a new one by default
            if (Pets.length === 0) {
                Pets.add(new Pet);
            }
        },
        events: {
            'click #create': 'create',
   ...