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
});
},
...