Backbone, Handlebars, Webshims

by Tony Tubbs

HTML

<script src="https://raw.github.com/documentcloud/underscore/1.4.3/underscore.js"></script>
<script src="https://raw.github.com/douglascrockford/JSON-js/master/json2.js"></script>
<script src="https://raw.github.com/documentcloud/backbone/1.0.0/backbone.js"></script>
<script src="http://afarkas.github.com/webshim/demos/js-webshim/minified/extras/modernizr-custom.js"></script>
<script src="http://afarkas.github.com/webshim/demos/js-webshim/minified/polyfiller.js"></script>
<script src="https://github.com/downloads/wycats/handlebars.js/handlebars-1.0.rc.1.js"></script>
<script type="text/x-handlebars" id="email-template">
{{#if email_required}}<span class="requiredPrompt">*</span>{{/if}}    
<input type="email"	class="emailAddress" value="{{email}}" placeholder="Email Address" maxlength="50" 
    {{#if email_required}}required{{/if}} />
{{#unless email_required}}
<a class="deleteEmail" href="javascript:void(0)">DEL</a>
{{/unless}}
<a class="addEmail" href="javascript:void(0)">ADD</a>
</script>	

<div id="appContainer" style="display; none;" >
		<div id="appHeader">HEADER</div>
		<form id="appContent">CONTENT</form>
		<div id="appFooter">FOOTER</div>
	</div>

CSS

#appHeader, #appFooter {
    background-color: lightblue;
}
.emailList .requiredPrompt {
    margin-left: -10px;
}
.emailList li {
    margin-left: 10px;
}
.user-error, .invalid-value {
    border-color: red
}

JavaScript

$.webshims.setOptions({
    waitReady: false
});
$.webshims.polyfill('forms');

var Email = Backbone.Model.extend({
    defaults: function () {
        return {
            email: "",
            email_required: false,
            email_invalid: ""
        };
    }
});

var EmailList = Backbone.Collection.extend({
    model: Email,
    // Custom collection properties
    required: 0,
    limit: 1,

    initialize: function (models, options) {
        // Save custom properties
        _.extend(this, _.pick(options, 'required', 'limit'));

        // Mark incoming models required up to options.required count
        $.each(models, function (index, model) {
            model.email_required = model.email_required || (index < options.required);
        });

        // Pad collection with empty models up to options.required count
        while (models.length < options.required) {
            models.push({
                email: "",
                email_required: true
            });
        }

        return this;
    }
});

var EmailItemView = Backbone.View.extend({
    tagName: 'li',

    events: {
        "change .emailAddress": "updateModel",
            "click  .deleteEmail": "deleteEmail"
    },

    initialize: function () {
        this.template = Handlebars.compile($('#email-template').text());
        this.listenTo(this.model, 'change', this.updateView);
        this.listenTo(this.model, 'destroy', this.remove);
    },

    render: function () {
        this.$el.htmlPolyfill(this.template(this.model.toJSON()));
        this.input = this.$el.find(".emailAddress");
        return this;
    },

    updateView: function () {
        $(this.input).val(this.model.get('email'));
        if (this.input.checkValidity) this.input.checkValidity();
        this.input.blur();
    },

    updateModel: function (e) {
        var value = this.input.val();
        this.model.set({
            email: value
        }, {
            validate: true
        });
    },

   ...