40
Backbone in Multipage Apps Michael Yagudaev @yagudaev [email protected] July 2013

Vanjs backbone-powerpoint

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Vanjs backbone-powerpoint

Backbone in Multipage Apps

Michael Yagudaev@yagudaev

[email protected]

July 2013

Page 2: Vanjs backbone-powerpoint

Outline About Me

Why Backbone?

Pain Points with Backbone

Design Patterns/Best Practices

Marionette.js

Page 3: Vanjs backbone-powerpoint

About Me (@yagudaev)

Co-founder of 0idle.com – an online marketplace for event organizers to find the perfect venue.

Built 0idle using Rails + Backbone.js

Entrepreneur and Rails Developer

Worked with Node.js

Started Winnipeg.js User Group

Consulting/Freelance work

Page 4: Vanjs backbone-powerpoint

Why Backbone.js? Simple

Flexible – use only the parts you need

Easy to integrate into existing code

Provides structure to your app

Proven

Well Documented (for the most part )

Good for hybrid apps

Page 5: Vanjs backbone-powerpoint

5

A Pain in the Back... Due to its un-opinionated approach, backbone

can be a real pain in the a**.

No clear guidelines

Lots of boilerplate code

Does not provide helpers to solve common problems

Easy to get memory leaks by not being careful when using events (zombie views)

Page 6: Vanjs backbone-powerpoint

Underline Principle for this talk

In any web system, the server should have the final say

Therefore, lets start developing server-side code first

Server-side code is easier to test (simple request/response)

Client-side functionality is to be considered as an added bonus in agile software development

You can do without backbone when you first start a project (KISS)

Assumption: a team of one

Page 7: Vanjs backbone-powerpoint

7

Design PatternsLets start simple and work our way up...

Page 8: Vanjs backbone-powerpoint

Scoping Your Selectors Problem: Overly generic jQuery selectors can

cause unexpected behavior when adding a new feature to a particular area of an application.

Example:

$(‘.btn-add’).click(addNewReply);

// Will conflict with:

$(‘.btn-add’).click(addNewMessage);

Solution: Use a backbone View

Page 9: Vanjs backbone-powerpoint

var MessageView = Backbone.View.exend({    el: $('#messages-page'),    events: { 'click .btn-add': 'addMessage' },    addMessage: function(ev) {        $target = $(ev.currentTarget)        // code to add message    }}); var RepliesView = Backbone.View.exend({    el: $('#replies-page'),    events: { 'click .btn-add': 'addReply' },    addMessage: function(ev) {        $target = $(ev.currentTarget)        // code to add message    }}); $(function() {    repliesView = new RepliesView();    messagesView = new MessagesView();});

Page 10: Vanjs backbone-powerpoint

File Structure Problem: Your one javascript file becomes long and

hard to maintain. You need a better way to separate concerns.

Solution: Break down project into folders based on object types in backbone with a main.js file that acts as the entry point of the application and defines a namespace.

Notes: you will need to use a make or a make like solution to stitch the files together (e.g. Rails asset pipeline or h5bp build tool).

Note 2: Keep it simple. Avoid using an AMD loader like RequireJS at this stage.

Page 11: Vanjs backbone-powerpoint

|____application.js -- manifest file (can have more than one)|____collections/|____lib/|____main.js|____models/|____templates/|____views/| |____messages_view.js| |____replies_view.js|____vendor/| |____backbone.js| |____jquery.js| |____underscore.js

File Structure (folders)

Page 12: Vanjs backbone-powerpoint

File Structure (manifest)In Rails:

//= require jquery//= require underscore//= require backbone//= require main//= require_directory ./models//= require_directory ./lib//= require_directory ./collections//= require_directory ./views

Page 13: Vanjs backbone-powerpoint

File Structure (files)window.APP_NAME = {    Models: {},    Collections: {},    Views: {},    Lib: {}  };

main.js -

views/messages_view.js -

APP_NAME.Views.MessagesView = Backbone.View.extend({...});

Page 14: Vanjs backbone-powerpoint

Template

Page 15: Vanjs backbone-powerpoint

Templates Problem: You need to dynamically generate

html content, but do not want pollute your js code with html markup.

Example: a dynamic file uploader allowing the user to upload any number of files.

Solution: place your html inside a script tag and use jQuery to extract the content of the template and then render it to the page. Alternatively you can use JST to give each template a separate file.

Page 16: Vanjs backbone-powerpoint

16

Templates<script id="photo-thumbnail-template" type="text/template">  <div class="thumbnail"> <img src="{{image_url}}" />    {{title}}  </div></script>

$thumbnail = $(Mustache.to_html($("#photo-thumbnail- template").html(), photo))$thumbnail.appendTo('.photos-preview.thumbnails')

Page 17: Vanjs backbone-powerpoint

17

Template Sharing

Page 18: Vanjs backbone-powerpoint

Template Sharing Problem: You have a template you want

rendered both on the client and server.

Example: you have a photo uploader that lets the user upload multiple photos and see photos that were already uploaded.

Solution: refactor the template into a mustache template and provide access to it from both the client and the server.

Limitation: cannot use helper methods (unless you are using Node.js)

Page 19: Vanjs backbone-powerpoint

19

Template Sharing//..<%- @photos.each do |photo| ->  <%= render 'photo_item', mustache: photo %><%- end %> //.. <script type="text/template" id="photo-item-template">  <%= render 'photo_item' %></script>

Page 20: Vanjs backbone-powerpoint

Client-side View Injection

Page 21: Vanjs backbone-powerpoint

Client-side View Injection

Problem: Similar to Template Sharing, but you are interested in re-using server-side helpers and do not care about filling in information in the template.

Example: a survey application which displays existing questions and allows to dynamically add new questions.

Solution: pre-render a partial somewhere in the DOM and to make it accessible to the client side code.

Page 22: Vanjs backbone-powerpoint

22

Client-side View Injection

// ...addSpace: function() {  var html = this.$('.btnAdd').data('view');  $('body').append(html);}// ...

<a href="#" class="btnAdd" data-view="<%= render 'space_card' %>">  Add Space</a>

Page 23: Vanjs backbone-powerpoint

View Helpers Problem: You want to change the format in

which your information is displayed in the view, but the logic will be too complicated to be added directly in the view.

Example: Format a date.

Solution: Define a view helper to handle this functionality.

Page 24: Vanjs backbone-powerpoint

24

// app/javascripts/helpers/application_helper.js

function formatDate(date) {

    moment(date).format("MMM Do YY");

// if using handlebars

Handlebars.registerHelper("formatDate", formatDate);

 

// Usage (in the templates):

{{formatDate date}}

Page 25: Vanjs backbone-powerpoint

Bootstrap Data Problem: You need to share data between the

client-side code and the server code. At the same time you would like to avoid incurring the cost of another HTTP request.

Example: You want to check if a user is logged in before you allow them to IM other users.

Solution: Use server-side processing to initialize your model(s).

Page 26: Vanjs backbone-powerpoint

26

<script>(function() {  var user;  // preprocessed file to bootstrap variables from ruby to javascript  window.ZI = {    Config: {},    Models: {},    Collections: {},    Views: {},    Lib: {},     currentUser: function() {      var user_data = <%=raw current_user ? current_user.to_json : 'null' %>;      if (user_data) return user = user || new ZI.Models.User(user_data);      return null;    }  };   // ... after the model has been loaded ...  console.log(ZI.currentUser() === null); // not logged in  console.log(ZI.currentUser() !== null); // logged in})();</script>

Page 27: Vanjs backbone-powerpoint

27

Bootstrap Data (e.g. 2)

  var photos = new Backbone.Collection;  photos.reset(<%= @photos.to_json %>);

Page 28: Vanjs backbone-powerpoint

Mixin Problem: You have code that is common

between several views or models, but it does not make sense to move code into a parent class.

Example: Code that allows you to open a message dialog and can be opened from several different views.

Solution: Use a mixin to include the common code in the target views or models.

Page 29: Vanjs backbone-powerpoint

29

ZI.Mixins.Navigation = {  openMessageDialog: function(ev) {    ev.preventDefault();    return $('#message-modal').modal();  }}; App.Views.VenuesView = Backbone.View.extend(  _.extend({}, App.Mixins.Navigation, {      //..})); App.Views.MessagesView = Backbone.View.extend(  _.extend({}, App.Mixins.Navigation, {      //...}));

Page 30: Vanjs backbone-powerpoint

30

Page 31: Vanjs backbone-powerpoint

Parent-Child (Sub Views) Views

Problem: You have a view that needs to communicate with another view and can be thought of as logically containing that view.

Example: Updating a dropdown after a user creates a new item through a modal dialog.

Solution: Create a reference to the child view from the parent view and listen on events fired by the child view.

Page 32: Vanjs backbone-powerpoint

32

ZI.Views.NewVenueModalView = Backbone.View.extend({  el: $('#new-venue-modal'),  events: { 'click .btn-primary': 'save' },  initialize: function() {    return this.formView = new ZI.Views.VenueFormView();  },  save: function() {    var _this = this;    return this.formView.save({      success: function() {        _this.trigger('save', _this.formView.model);        return _this.close();      },      error: function() {        return _this.trigger('error', arguments);      }    });  },  open: function() { return this.$el.modal(); },  close: function() { return this.$el.modal('hide'); }});// singleton pattern ZI.Views.NewVenueModalView.getInstance = function() {  return this.instance != null ? this.instance : this.instance = new this;};

Child View

Page 33: Vanjs backbone-powerpoint

33

Parent View

var openNewVenueDialog = function(e, data) {  var new_venue_modal_view = ZI.Views.NewVenueModalView.getInstance();  new_venue_modal_view.on('save', function(model) { $('#space_venue_id').append("<option>" + (model.get('name')) + "</ option>");    $('#space_venue_id').val(model.get('id'));  });  new_venue_modal_view.open();};

Page 34: Vanjs backbone-powerpoint

34

Two-Way Binding

Page 35: Vanjs backbone-powerpoint

Two-Way Bindings Problem: You need to dynamically and

automatically update a UI element when the underling data changes.

Example: Recalculate and display the total amount in a shopping cart if the quantity of any of the items changes.

Solution: Use a two-way binding plugin for backbone called backbone.stickit.

Page 36: Vanjs backbone-powerpoint

36

ZI.Views.EventDetailsBookView = Backbone.View.extend({  el: $('#event-details-booking-page'),  model: new Booking(),  bindings: {    '.total-price': {      observe: ['start_date', 'start_time', 'end_date', 'end_time'],      onGet: function(values) {        return '$' + this.calculateHoursBooked(values) * this.hourly_price;      }    },    '#booking_start_date': 'start_date', '#booking_end_date': 'end_date',    '#booking_start_time': 'start_time', '#booking_end_time': 'end_time'  },  initialize: function() {    this.hourly_price = this.$el.data('hourly-price');    return this.stickit();  },  calculateHoursBooked: function(values) {//...  }});

Page 37: Vanjs backbone-powerpoint

Marionette.js Provides architectural infrastructure for

backbone.

View Management Layouts

Regions

Specialized View Types

Memory management

Messaging

Application - controllers, modules, etc

Page 38: Vanjs backbone-powerpoint

38

QUESTIONS

Page 39: Vanjs backbone-powerpoint

References Railscasts -

http://railscasts.com/episodes/196-nested-model-form-revised

Backbone Patterns: http://ricostacruz.com/backbone-patterns/

Backbone.js - http://backbonejs.org/

Marionette - http://marionettejs.com/

Backbone.Stickit - http://nytimes.github.io/backbone.stickit/

Page 40: Vanjs backbone-powerpoint

40

References Handlebars - http://handlebarsjs.com/

Mustache - http://mustache.github.io/