UNPKG

viewbridge

Version:

Shares views or templates between the server and client, making client side template pre-compilation and/or server side view pre-rendering easy.

172 lines (141 loc) 4.45 kB
extends ../layout block content h1= title form#movie_form.pure-form(action='/movies', method='post') fieldset legend Add a Movie input(type='text', name='title', placeholder='Title') input(type='text', name='studio', placeholder='Studio') label input(type='checkbox', name='like') | Liked? button.pure-button.pure-button-primary(type='submit') Add table#movie_table.pure-table thead tr th Studio th Title th Liked? tbody each movie in movies tr(id="movie_#{movie.id}") // Yes, `with` is deprecated and frowned upon. // Jade used to have partials and this works in lieu of them. - with (movie) { //- View/Template used on both server and client include table_row - } block scripts script(src='/javascripts/vendor/jquery.js') script(src='/javascripts/vendor/lodash.underscore.js') script(src='/javascripts/vendor/backbone.js') // Templates exported and precompiled via Viewbridge. script(src='/javascripts/templates.js') script var app = app || {}; void function() { 'use strict' // // Model - Movie // app.Movie = Backbone.Model.extend(); // // Collection - Movies // app.movies = function() { var Movies = Backbone.Collection.extend({ model: app.Movie }); return new Movies(); }(); // // View - Movie Row // app.MovieRow = Backbone.View.extend({ tagName: 'tr', initialize: function() { this.listenTo(this.model, 'change', this.render); }, events: { 'click .like': 'toggleLike' }, render: function() { // Viewbridge! // This is the very same template used on the server. this.el.innerHTML = viewbridge.movies.table_row(this.model.toJSON()); return this; }, toggleLike: function() { var like = this.model.get('like'); this.model.set('like', ! like); } }); // // View - Movie Table // app.MovieTable = Backbone.View.extend({ el: '#movie_table', initialize: function() { this.tbody = this.el.querySelector('tbody'); this.listenTo(app.movies, 'add', this.add); this.listenTo(app.movies, 'reset', this.reset); }, add: function(model) { var row = new app.MovieRow({model: model}); this.tbody.appendChild( row.render().el ); }, bootstrap: function() { app.movies.each(function(model) { var el = document.getElementById('movie_' + model.id); if (el === null) return; // Since the view was already rendered on the server, we just // need to initialize the Backbone View with the model and the // DOM element. new app.MovieRow({model: model, el: el}); }); }, reset: function(collection, options) { if (options.bootstrap) { this.bootstrap(); } else { this.tbody.innerHTML = ''; app.movies.each(this.add, this); } } }); // // View - Movie Form // app.MovieForm = Backbone.View.extend({ el: '#movie_form', initialize: function() { this.title = this.el.querySelector('[name=title]'); this.studio = this.el.querySelector('[name=studio]'); this.like = this.el.querySelector('[name=like]'); }, events: { 'click [type=submit]': 'add', }, add: function(evt) { evt.preventDefault(); app.movies.add({ title: this.title.value, studio: this.studio.value, like: this.like.checked }); this.el.reset(); } }); var bootstrapData = !{JSON.stringify(movies)}; // // Initialize the app // $(function() { new app.MovieTable(); new app.MovieForm(); // Bootstrap the collection with initial data. Pass a custom // flag to indicate that we only want to initialize the relevant Views // and not render them. app.movies.reset(bootstrapData, {bootstrap: true}); }); }();