Download - Make your Backbone Application dance

Transcript
Page 1: Make your Backbone Application dance

ake your Backbone Application Dance

May 15th, 2014 — Verona

Nicholas Valbusa@squallstar

Page 2: Make your Backbone Application dance

I AM

Nicholas Valbusasquallstar - squallstar.it

AT

Lead JS Developercronycle.com

Page 3: Make your Backbone Application dance

Our challenges:

- Expand your timeline tweets into stories

- Organize and filter your stories into collections

"Cronycle is a content curation tool based on Twitter tweets and RSS feeds"

- Make it real-time, responsive, awesome

- Live search

Page 4: Make your Backbone Application dance

10ppl in London

- Rails+Marionette Webclient

Paid service, at the end of this speech (Yipee!!!)

- Rails API, Elasticsearch, Redis queue

- iOS Native app

Page 5: Make your Backbone Application dance

Always choose the right tool for the job

Page 6: Make your Backbone Application dance
Page 7: Make your Backbone Application dance

• Solid foundation/core classes

• Flexible and simple

• Great pub/sub events architecture

• Awesome Model/Collection implementationfor REST resources

• Good MV*-style code separation (p.s. it’s a MVP)

• It is not like Angular.JS

THE GOOD PARTS

Page 8: Make your Backbone Application dance

• It's incredibly easy to end up in a bad place

• No Application main class (some people use its router)

• Doesn’t include automatic and good ways to bind models to their views

• No “native” way to organise the pieces of your webapp into modules

• No "native" way to organize layouts (header/footer/sidebar/content/etc..)

• It is not sponsored/maintained by Google

THE BAD PARTS

Page 9: Make your Backbone Application dance

let me read that again,

• “Awesome Model/Collection implementationfor REST resources”

var Library = Backbone.Collection.extend({ model: Book, url: “v1/books” });

Page 10: Make your Backbone Application dance

for all the rest, there’s…

Page 11: Make your Backbone Application dance

Marionette.jsmarionettejs.com

Page 12: Make your Backbone Application dance

Very similar to Backbone, just goes a few more steps.

Page 13: Make your Backbone Application dance

“A composite application library for Backbone that aims to simplify the construction of large scale

JavaScript applications” !

— that sits on top of Backbone

An event-driven collection of common design and implementation patterns.

Basically… Marionette brings an application architecture to Backbone

Page 14: Make your Backbone Application dance

Key features

• Organised as hellApplications are built in modules, and with event-driven architecture

• No zombies Built-in memory management and zombie-killing in views, regions and layouts

• Flexible Just code the way you prefer, and picks only the features you need.

• Takes care of the rendering process

Page 15: Make your Backbone Application dance

TAKES CARE OF THE RENDERING

PROCESS

Page 16: Make your Backbone Application dance

Depends on:

Backbone & Underscore

Backbone.BabySitter & Backbone.Wreqr (both included)

just 31kb !

Page 17: Make your Backbone Application dance

PrefaceThe base structure I'm using was adopted from BackboneRails

Page 18: Make your Backbone Application dance

App skeleton& boot

Page 19: Make your Backbone Application dance

<html> <head> <title>JSDay2014 - Marionette</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script src="libs/jquery.js"></script> <script src="libs/underscore.js"></script> <script src="libs/backbone.js"></script> <script src="libs/backbone.marionette.js"></script> <script src="boot.js"></script> </head> <body>

<div id="header-region"></div> <div id="main-region"></div> <div id="footer-region"></div>

<script> App.start({ environment: 'staging', foo: 'bar' }); </script> ! </body> </html>

index.html

Page 20: Make your Backbone Application dance

App = new Backbone.Marionette.Application(); !App.on("initialize:before", function (options) { // do something. come on! }); !App.on("initialize:after", function (options) { if (Backbone.history){ Backbone.history.start(); } });

javascript / boot.js

— that’s it!

Page 21: Make your Backbone Application dance

Regions

Page 22: Make your Backbone Application dance

App.addRegions({ headerRegion: "#header-region", mainRegion: "#main-region" footerRegion: "#footer-region" });

MyCustomHeaderRegion = Marionette.Region.extend({ el: "#header-region" }); !MyApp.addRegions({ headerRegion: MyCustomHeaderRegion });

You can also define custom classes for your regions:

“Regions provide consistent methods to manage, show and close views in your applications and layouts”

Page 23: Make your Backbone Application dance

header-region

main-region

footer-region

Page 24: Make your Backbone Application dance

Showing a view in a region

var myView = new MyView(); !// renders and displays the view App.mainRegion.show(myView); !// closes the current view App.mainRegion.close();

— simple as that.

Page 25: Make your Backbone Application dance

header-regionmain-region

footer-region

MyView

App.mainRegion.currentView

Page 26: Make your Backbone Application dance

If you replace the current view with a new view by calling show, it will automatically close the previous view

// Show the first view. var myView = new MyView(); MyApp.mainRegion.show(myView);

no more zombies!

// Replace the view with another. The // `close` method is called for you var anotherView = new AnotherView(); MyApp.mainRegion.show(anotherView);

Page 27: Make your Backbone Application dance

Marionette Modules

Page 28: Make your Backbone Application dance

AMD/Require vs Marionette Modules

Take advantage of Marionette's built in module-loader

App.module("MyModule", function (MyModule, App, Backbone, Marionette, $, _) { ! // do stuff here ... ! var myData = "this is private data"; ! MyModule.someData = "public data"; !}); !var theSameModule = MyApp.module("MyModule");

Page 29: Make your Backbone Application dance

Split your sections/features into modules

Page 30: Make your Backbone Application dance

Always keep your app organised —

while it grows

Page 31: Make your Backbone Application dance

Organise each module

App.BlogApp (Marionette Module)BlogApp.Router (Marionette.AppRouter)BlogApp.Posts (Submodule)

Posts.ControllerPosts.View

Page 32: Make your Backbone Application dance

What about Backbone models/collections?

App.Entities (Marionette module)Entities.Article (Backbone.Model)Entities.Articles (Backbone.Collection)

Page 33: Make your Backbone Application dance

yeah, about those models...

Page 34: Make your Backbone Application dance

The magic of Backbone models

Page 35: Make your Backbone Application dance

App.module("Entities", function (Entities, App, Backbone, Marionette, $, _) { ! Entities.Post = Backbone.Model.extend(); ! Entities.Posts = Backbone.Collection.extend({ model: Entities.Post, url: “path/to/posts.json“ }); !});

entities / posts.js

Page 36: Make your Backbone Application dance

App.module("BlogApp", function (BlogApp, App, Bk, Mr, $, _) { ! BlogApp.Router = Backbone.Marionette.AppRouter.extend({ appRoutes: { "posts" : "showArticles", "posts/:id" : "showArticle" } }); ! var API = { showArticles: function () { BlogApp.Posts.Controller.Show(); }, showArticle: function (id) { BlogApp.Posts.Controller.Show(id); } }; ! App.addInitializer(function () { new BlogApp.Router({ controller: API }); }); !});

apps / blog / app.js

Page 37: Make your Backbone Application dance

App.module("BlogApp.Posts", function (Posts, App, Bk, Mr, $, _) { ! Posts.Controller = { Show: function () { ! var layout = new Posts.View({ collection: new App.Entities.Posts }); ! App.mainRegion.show(layout); } }; !});

apps / blog / posts / controller.js

Page 38: Make your Backbone Application dance

App.module("BlogApp.Posts", function (Posts, App, Backbone, M, $, _) { ! Posts.View = Backbone.Marionette.View.extend({ tagName: "section", className: "posts" }); !});

apps / blog / posts / view.js

Page 39: Make your Backbone Application dance

Here comes the magic!

Let’s have a look at Marionette Views

Page 40: Make your Backbone Application dance

Marionette.ItemViewRenders a single item

(Backbone.Model)

Backbone.Model

ItemView

Page 41: Make your Backbone Application dance

Marionette.CollectionViewRenders the items of a Backbone.Collection

Doesn’t need a template

CollectionView

ItemView Backbone.Model

Backbone.Collection

ItemView Backbone.Model

Page 42: Make your Backbone Application dance

Marionette.CompositeView

Renders the items of a Backbone.Collection within a wrapper

Extends from Marionette.CollectionView !

Also: Represents both a branch and a tree structure

Therefore: can also render a model if needed

Page 43: Make your Backbone Application dance

CollectionView

ItemViewBackbone.Model

Backbone.Collection

ItemViewBackbone.Model

ItemViewBackbone.Model

CompositeViewTemplate

+ Backbone.Collection + optional Backbone.Model

Backbone.Model

ItemView

.some-selector

Backbone.Model

ItemView

Page 44: Make your Backbone Application dance

Before going further, choose your template engine

Page 45: Make your Backbone Application dance

Underscore templates works out of the box

<script type="template" id="post-template"> <h2> <%- title %> </h2> </script>

Page 46: Make your Backbone Application dance

you can also override Marionette Renderer:

Backbone.Marionette.Renderer.render = function (template, data) { ! tpl = _.template($( "script.wtf-is-this-" + template ).html()); if (!tpl) throw("Template " + template + " not found!"); ! return tpl(data); !};

<script type="text/template" class="wtf-is-this-post-template"> <h2> <%- title %> </h2> </script>

config/marionette/renderer.js

Page 47: Make your Backbone Application dance

Using Rails? Go with Jade + JST

gem 'tilt-jade'

Compiles jade templates into js functions for use as clientside templates

Page 48: Make your Backbone Application dance

Jade is just amazing

.post-content header(class='ng-wtf') h1= title span by #{author} ! if youAreUsingJade p You are amazing ! .body= description

Page 49: Make your Backbone Application dance

Backbone.Marionette.Renderer.render = (tpl, data) -> path = JST["apps/" + tpl] throw "Template #{tpl} not found!" unless path path data

CoffeeScript...

Page 50: Make your Backbone Application dance

back to our applet's implement these views

Page 51: Make your Backbone Application dance

App.module("BlogApp.Posts", function (Posts, App, Bk, Mr, $, _) { ! Posts.PostView = Backbone.Marionette.ItemView.extend({ tagName: "article", className: "post", template: “#post-template" }); ! Posts.View = Backbone.Marionette.CollectionView.extend({ tagName: "section", className: "posts", itemView: Posts.PostView, ! initialize: function (options) { options.collection.fetch(); } }); !});

apps / blog / posts/ view.js

Page 52: Make your Backbone Application dance
Page 53: Make your Backbone Application dance
Page 54: Make your Backbone Application dance

let’s make it better

<script type="text/template" id="post-template"> <a href="#"><%- title %></a> </script> !!<script type="text/template" id="posts-template"> <h1>My nice blog</h1> <ul></ul> </script>

Page 55: Make your Backbone Application dance

Posts.View = Backbone.Marionette.CompositeView.extend({ tagName: "section", className: "posts", template: “#posts-template", itemView: Posts.PostView, itemViewContainer: "ul", ! initialize: function (options) { options.collection.fetch(); } });

just a few changes to the CollectionView

Page 56: Make your Backbone Application dance

Posts.PostView = Backbone.Marionette.ItemView.extend({ tagName: "li", className: "post", template: “#post-template", events: { "click a" : "showSinglePost" }, showSinglePost: function (event) { event.preventDefault(); Backbone.history.navigate("posts/" + this.model.get('id')); } });

and some more to the ItemView

Page 57: Make your Backbone Application dance
Page 58: Make your Backbone Application dance

Serializing the data

Marionette calls model.toJSON() by default

Posts.PostView = Backbone.Marionette.ItemView.extend({ ... ! // overrides the default behaviour serializeData: function () { return _.extend(this.model.toJSON(), { "foo" : "bar" }); } });

can be overridden by defining serializeData()

Page 59: Make your Backbone Application dance

Template helpers

<script id="my-template" type="template"> I think that <%= showMessage() %> </script>

Posts.PostView = Backbone.Marionette.ItemView.extend({ ... ! templateHelpers: { showMessage: function () { return this.title + " rocks!"; } }, ! ... });

Page 60: Make your Backbone Application dance

Modal/Collection events

Backbone.Marionette.CompositeView.extend({ ! modelEvents: { // eq to view.listenTo(view.model, "change:name", view.nameChanged, view) "change:name": "nameChanged" }, ! collectionEvents: { // eq to view.listenTo(view.collection, "add", view.itemAdded, view) "add": "itemAdded" }, ! // ... event handler methods nameChanged: function () { /* ... */ }, itemAdded: function () { /* ... */ } !});

Page 61: Make your Backbone Application dance

App global requests

// define your request App.reqres.setHandler("show:post", function (id) { Backbone.history.navigate("posts/" + id, true); });

AKA let your modules talk with each other

// use it App.request("show:post", 3);

Page 62: Make your Backbone Application dance

Marionette in the real world

— 5 minutes of Marionette applied to Cronycle —

Page 63: Make your Backbone Application dance

header-region with ItemView (User, Backbone.Model)

main-region with CollectionView (Backbone.Collection)

CompositeView (Model + Collection)

ItemView (Model)

ItemView (Model)

Page 64: Make your Backbone Application dance

left-sidebar-region with CompositeView

Page 65: Make your Backbone Application dance

Modal windows, just an overlay region

Page 66: Make your Backbone Application dance

Modal region

App.module("Modals", function (Modals, App, Backbone, Marionette, $, _) { ! Modals.Region = Marionette.Region["extends"]({ el: "#modal-region", open: function(view) { $.magnificPopup.open(view); }, close: function() { $.magnificPopup.instance.close(); } }); !!!!!!!!!!!!!!!!});

Modals.start = function () { App.addRegions({modalRegion: Modals.Region}); };

App.reqres.setHandler("alert", function (title) { view = new MyModalWindow({ title: title }); return App.modalRegion.show(view); });

// This is how you use it App.request("alert", "Roflcopter!");

Page 67: Make your Backbone Application dance

Fetching for new articles

Page 68: Make your Backbone Application dance

1. define a comparator on your collection

Fetching for new articles

var Entities.Posts = Backbone.Collection.extends({ model: Entities.Post, url: "/posts", ! comparator: function (model) { return -parseInt(model.get('published_at'), 10); } });

Page 69: Make your Backbone Application dance

2. define a custom fetch method

Fetching for new articles

var Entities.Posts = Backbone.Collection.extends({ ! fetchNewPosts: function (callback) { this.fetch({ url: "posts/?min_ts=#{@first().get('published_at')}", update: true, add: true, remove: false } !});

Page 70: Make your Backbone Application dance

Fetching for new articles

3. override the appendHtml method on your CompositeView

var YourItemView = Backbone.Marionette.CompositeView.extends({ ! ui: { linksContainer: ".posts-container" }, ! appendHtml: function (collectionView, itemView, index) { if (index == 0){ this.ui.linksContainer.prepend(itemView.$el); } else { childAtIndex = this.ui.linksContainer.find("> article:eq(" + index + ")"); ! if (childAtIndex.length) { childAtIndex.before(itemView.$el); } else { this.ui.linksContainer.append(itemView.$el); } } } !});

Page 71: Make your Backbone Application dance

put a test on it

https://github.com/bradphelan/jasminerice

+ jasmine-rice for Rails users

If you like your goat...

Page 72: Make your Backbone Application dance

describe("Navigating to the posts route", function () { ! it("should display some articles", function () { ! Backbone.history.navigate("/posts", true); ! expect(App.mainRegion.$el.find('article.post').length).toBeGreaterThan(0); ! expect(App.mainRegion.currentView.collection.at(0).get('title')).toBe('foo'); ! }); });

Page 73: Make your Backbone Application dance

github.com/squallstar/jsday2014-marionettejs

The project we just created:

(the dummy blog, not cronycle!)

Nicholas Valbusa@squallstar - squallstar.it

THANKS! Q/A?

https://joind.in/11286