SlideShare a Scribd company logo
ake your Backbone
Application Dance
May 15th, 2014 — Verona
Nicholas Valbusa
@squallstar
I AM
Nicholas Valbusa
squallstar - squallstar.it
AT
Lead JS Developer
cronycle.com
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
10ppl in London
- Rails+Marionette Webclient
Paid service,
at the end of this speech (Yipee!!!)
- Rails API, Elasticsearch, Redis queue
- iOS Native app
Always choose the
right tool for the job
Make your Backbone Application dance
• Solid foundation/core classes
• Flexible and simple
• Great pub/sub events architecture
• Awesome Model/Collection implementation

for REST resources
• Good MV*-style code separation (p.s. it’s a MVP)
• It is not like Angular.JS
THE GOOD PARTS
• 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
let me read that again,
• “Awesome Model/Collection implementation

for REST resources”
var Library = Backbone.Collection.extend({
model: Book,
url: “v1/books”
});
for all the rest, there’s…
Marionette.js
marionettejs.com
Very similar to Backbone, just
goes a few more steps.
“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
Key features
• Organised as hell

Applications 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
TAKES CARE OF
THE RENDERING
PROCESS
Depends on:
Backbone & Underscore
Backbone.BabySitter & Backbone.Wreqr

(both included)
just 31kb !
Preface
The base structure I'm using was adopted from BackboneRails
App skeleton
& boot
<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
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!
Regions
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”
header-region
main-region
footer-region
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.
header-region
main-region
footer-region
MyView
App.mainRegion.currentView
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);
Marionette Modules
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");
Split your sections/features into modules
Always keep your
app organised —
while it grows
Organise each module
App.BlogApp (Marionette Module)
BlogApp.Router (Marionette.AppRouter)
BlogApp.Posts (Submodule)
Posts.Controller
Posts.View
What about Backbone models/collections?
App.Entities (Marionette module)
Entities.Article (Backbone.Model)
Entities.Articles (Backbone.Collection)
yeah, about those models...
The magic of Backbone models
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
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
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
App.module("BlogApp.Posts", function (Posts, App, Backbone, M, $, _) {
!
Posts.View = Backbone.Marionette.View.extend({
tagName: "section",
className: "posts"
});
!
});
apps / blog / posts / view.js
Here comes the magic!
Let’s have a look at Marionette Views
Marionette.ItemView
Renders a single item
(Backbone.Model)
Backbone.Model
ItemView
Marionette.CollectionView
Renders the items of a Backbone.Collection
Doesn’t need a template
CollectionView
ItemView Backbone.Model
Backbone.Collection
ItemView Backbone.Model
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
CollectionView
ItemView
Backbone.Model
Backbone.Collection
ItemView
Backbone.Model
ItemView
Backbone.Model
CompositeView
Template

+ Backbone.Collection
+ optional Backbone.Model
Backbone.Model
ItemView
.some-selector
Backbone.Model
ItemView
Before going further, choose your
template engine
Underscore templates works out of the box
<script type="template" id="post-template">
<h2>
<%- title %>
</h2>
</script>
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
Using Rails? Go with Jade + JST
gem 'tilt-jade'
Compiles jade templates into js functions
for use as clientside templates
Jade is just amazing
.post-content
header(class='ng-wtf')
h1= title
span by #{author}
!
if youAreUsingJade
p You are amazing
!
.body= description
Backbone.Marionette.Renderer.render = (tpl, data) ->
path = JST["apps/" + tpl]
throw "Template #{tpl} not found!" unless path
path data
CoffeeScript...
back to our app
let's implement these views
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
Make your Backbone Application dance
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>
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
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
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()
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!";
}
},
!
...
});
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 () { /* ... */ }
!
});
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);
Marionette in the real world
— 5 minutes of Marionette applied to Cronycle —
header-region with ItemView (User, Backbone.Model)
main-region with CollectionView
(Backbone.Collection)
CompositeView (Model +
Collection)
ItemView (Model)
ItemView (Model)
left-sidebar-region
with CompositeView
Modal windows, just an overlay region
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!");
Fetching for new articles
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);
}
});
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
}
!
});
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);
}
}
}
!
});
put a test on it
https://github.com/bradphelan/jasminerice
+ jasmine-rice for Rails users
If you like your goat...
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');
!
});
});
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

More Related Content

PDF
Marionette: Building your first app
PDF
Marionette: the Backbone framework
PPTX
Backbone And Marionette : Take Over The World
PPTX
Backbone/Marionette recap [2015]
PDF
Scalable CSS You and Your Back-End Coders Can Love - @CSSConf Asia 2014
PDF
The Art of AngularJS in 2015 - Angular Summit 2015
PPT
SenchaCon 2016: Expect the Unexpected - Dealing with Errors in Web Apps
PDF
Html5 and beyond the next generation of mobile web applications - Touch Tou...
Marionette: Building your first app
Marionette: the Backbone framework
Backbone And Marionette : Take Over The World
Backbone/Marionette recap [2015]
Scalable CSS You and Your Back-End Coders Can Love - @CSSConf Asia 2014
The Art of AngularJS in 2015 - Angular Summit 2015
SenchaCon 2016: Expect the Unexpected - Dealing with Errors in Web Apps
Html5 and beyond the next generation of mobile web applications - Touch Tou...

What's hot (18)

PDF
Gitter marionette deck
PDF
Javascript framework and backbone
PDF
The Point of Vue - Intro to Vue.js
PDF
AtlasCamp 2010: Making Confluence Macros Easy (for the user) - Dave Taylor
PPT
Vue.js Getting Started
PDF
Invitation of the BEAR.sunday's world
PDF
Enjoy the vue.js
TXT
New text documentfsdfs
PDF
Agile documentation with Confluence and Sparx Enterprise Architect
PPTX
BACKBONE.JS & UNDERSCORE.JS
PDF
Introduction to AngularJS
PDF
iPhone Appleless Apps
PDF
Angular JS blog tutorial
PPTX
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
PPT
Creating the interfaces of the future with the APIs of today
PDF
Gettings started with the superheroic JavaScript library AngularJS
PDF
Javascript MVC & Backbone Tips & Tricks
PPT
You Know WebOS
Gitter marionette deck
Javascript framework and backbone
The Point of Vue - Intro to Vue.js
AtlasCamp 2010: Making Confluence Macros Easy (for the user) - Dave Taylor
Vue.js Getting Started
Invitation of the BEAR.sunday's world
Enjoy the vue.js
New text documentfsdfs
Agile documentation with Confluence and Sparx Enterprise Architect
BACKBONE.JS & UNDERSCORE.JS
Introduction to AngularJS
iPhone Appleless Apps
Angular JS blog tutorial
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
Creating the interfaces of the future with the APIs of today
Gettings started with the superheroic JavaScript library AngularJS
Javascript MVC & Backbone Tips & Tricks
You Know WebOS
Ad

Viewers also liked (20)

PPTX
Meadowlark overview
PDF
Folleto Sap Hr
PPT
Introduction to kb
PPTX
Inf1100 week6 books
DOCX
Project gallery
PPS
In Love
PPTX
Career Presentation
PPT
Web site revised 5 10-13
PPTX
Inf1100 week6 books
PPTX
Senior project presentation
PPTX
Inf1100 week9 apa plagiarism
KEY
Webkit meets native development
PPTX
LinkedDataBR Kettle
DOCX
Project gallery mj donofrio
KEY
The Price of Connectivity
PDF
Graduate student writing: Finding your academic voice
PPT
Africacityapps presentation
PPT
Pengapian sepeda motor
PPTX
Introducción al algebra (michael) cooregido
PDF
Ejercicios schaum algebra_lineal
Meadowlark overview
Folleto Sap Hr
Introduction to kb
Inf1100 week6 books
Project gallery
In Love
Career Presentation
Web site revised 5 10-13
Inf1100 week6 books
Senior project presentation
Inf1100 week9 apa plagiarism
Webkit meets native development
LinkedDataBR Kettle
Project gallery mj donofrio
The Price of Connectivity
Graduate student writing: Finding your academic voice
Africacityapps presentation
Pengapian sepeda motor
Introducción al algebra (michael) cooregido
Ejercicios schaum algebra_lineal
Ad

Similar to Make your Backbone Application dance (20)

PPTX
MV* presentation frameworks in Javascript: en garde, pret, allez!
PDF
Ember vs Backbone
PDF
Yeoman AngularJS and D3 - A solid stack for web apps
PPTX
BackboneJS Training - Giving Backbone to your applications
PDF
WebNet Conference 2012 - Designing complex applications using html5 and knock...
PPTX
Writing HTML5 Web Apps using Backbone.js and GAE
PDF
Rp 6 session 2 naresh bhatia
PPTX
jquery summit presentation for large scale javascript applications
PDF
Backbone.js
PDF
MSc Enterprise Systems Development Guest Lecture at UniS (2/12/09)
PPTX
Backbone.js
PPTX
Javascript Design Patterns
PPTX
Creating Single Page Web App using Backbone JS
PDF
Angular Directives from Scratch
PDF
Intro to BackboneJS + Intermediate Javascript
PDF
Developing maintainable Cordova applications
PPTX
Angular - Beginner
PDF
Building Better Web Apps with Angular.js (SXSW 2014)
PDF
Solid And Sustainable Development in Scala
PDF
Developing large scale JavaScript applications
MV* presentation frameworks in Javascript: en garde, pret, allez!
Ember vs Backbone
Yeoman AngularJS and D3 - A solid stack for web apps
BackboneJS Training - Giving Backbone to your applications
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Writing HTML5 Web Apps using Backbone.js and GAE
Rp 6 session 2 naresh bhatia
jquery summit presentation for large scale javascript applications
Backbone.js
MSc Enterprise Systems Development Guest Lecture at UniS (2/12/09)
Backbone.js
Javascript Design Patterns
Creating Single Page Web App using Backbone JS
Angular Directives from Scratch
Intro to BackboneJS + Intermediate Javascript
Developing maintainable Cordova applications
Angular - Beginner
Building Better Web Apps with Angular.js (SXSW 2014)
Solid And Sustainable Development in Scala
Developing large scale JavaScript applications

Recently uploaded (20)

PPTX
Mathew Digital SEO Checklist Guidlines 2025
PPTX
artificial intelligence overview of it and more
PDF
📍 LABUAN4D EXCLUSIVE SERVER STAR GAMING ASIA NO.1 TERPOPULER DI INDONESIA ! 🌟
PPTX
Funds Management Learning Material for Beg
PDF
Slides PDF: The World Game (s) Eco Economic Epochs.pdf
PDF
SlidesGDGoCxRAIS about Google Dialogflow and NotebookLM.pdf
PDF
SASE Traffic Flow - ZTNA Connector-1.pdf
PPTX
Internet Safety for Seniors presentation
PPTX
artificialintelligenceai1-copy-210604123353.pptx
PDF
📍 LABUAN4D EXCLUSIVE SERVER STAR GAMING ASIA NO.1 TERPOPULER DI INDONESIA ! 🌟
PDF
Exploring VPS Hosting Trends for SMBs in 2025
PDF
simpleintnettestmetiaerl for the simple testint
PPTX
Introduction to cybersecurity and digital nettiquette
PPTX
SAP Ariba Sourcing PPT for learning material
PPT
FIRE PREVENTION AND CONTROL PLAN- LUS.FM.MQ.OM.UTM.PLN.00014.ppt
PPT
Ethics in Information System - Management Information System
PPTX
Slides PPTX: World Game (s): Eco Economic Epochs.pptx
PPTX
IPCNA VIRTUAL CLASSES INTERMEDIATE 6 PROJECT.pptx
PPT
250152213-Excitation-SystemWERRT (1).ppt
PDF
Introduction to the IoT system, how the IoT system works
Mathew Digital SEO Checklist Guidlines 2025
artificial intelligence overview of it and more
📍 LABUAN4D EXCLUSIVE SERVER STAR GAMING ASIA NO.1 TERPOPULER DI INDONESIA ! 🌟
Funds Management Learning Material for Beg
Slides PDF: The World Game (s) Eco Economic Epochs.pdf
SlidesGDGoCxRAIS about Google Dialogflow and NotebookLM.pdf
SASE Traffic Flow - ZTNA Connector-1.pdf
Internet Safety for Seniors presentation
artificialintelligenceai1-copy-210604123353.pptx
📍 LABUAN4D EXCLUSIVE SERVER STAR GAMING ASIA NO.1 TERPOPULER DI INDONESIA ! 🌟
Exploring VPS Hosting Trends for SMBs in 2025
simpleintnettestmetiaerl for the simple testint
Introduction to cybersecurity and digital nettiquette
SAP Ariba Sourcing PPT for learning material
FIRE PREVENTION AND CONTROL PLAN- LUS.FM.MQ.OM.UTM.PLN.00014.ppt
Ethics in Information System - Management Information System
Slides PPTX: World Game (s): Eco Economic Epochs.pptx
IPCNA VIRTUAL CLASSES INTERMEDIATE 6 PROJECT.pptx
250152213-Excitation-SystemWERRT (1).ppt
Introduction to the IoT system, how the IoT system works

Make your Backbone Application dance

  • 1. ake your Backbone Application Dance May 15th, 2014 — Verona Nicholas Valbusa @squallstar
  • 2. I AM Nicholas Valbusa squallstar - squallstar.it AT Lead JS Developer cronycle.com
  • 3. 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
  • 4. 10ppl in London - Rails+Marionette Webclient Paid service, at the end of this speech (Yipee!!!) - Rails API, Elasticsearch, Redis queue - iOS Native app
  • 5. Always choose the right tool for the job
  • 7. • Solid foundation/core classes • Flexible and simple • Great pub/sub events architecture • Awesome Model/Collection implementation
 for REST resources • Good MV*-style code separation (p.s. it’s a MVP) • It is not like Angular.JS THE GOOD PARTS
  • 8. • 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
  • 9. let me read that again, • “Awesome Model/Collection implementation
 for REST resources” var Library = Backbone.Collection.extend({ model: Book, url: “v1/books” });
  • 10. for all the rest, there’s…
  • 12. Very similar to Backbone, just goes a few more steps.
  • 13. “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
  • 14. Key features • Organised as hell
 Applications 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
  • 15. TAKES CARE OF THE RENDERING PROCESS
  • 16. Depends on: Backbone & Underscore Backbone.BabySitter & Backbone.Wreqr
 (both included) just 31kb !
  • 17. Preface The base structure I'm using was adopted from BackboneRails
  • 19. <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
  • 20. 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!
  • 22. 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”
  • 24. 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.
  • 26. 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);
  • 28. 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");
  • 30. Always keep your app organised — while it grows
  • 31. Organise each module App.BlogApp (Marionette Module) BlogApp.Router (Marionette.AppRouter) BlogApp.Posts (Submodule) Posts.Controller Posts.View
  • 32. What about Backbone models/collections? App.Entities (Marionette module) Entities.Article (Backbone.Model) Entities.Articles (Backbone.Collection)
  • 33. yeah, about those models...
  • 34. The magic of Backbone models
  • 35. 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
  • 36. 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
  • 37. 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
  • 38. App.module("BlogApp.Posts", function (Posts, App, Backbone, M, $, _) { ! Posts.View = Backbone.Marionette.View.extend({ tagName: "section", className: "posts" }); ! }); apps / blog / posts / view.js
  • 39. Here comes the magic! Let’s have a look at Marionette Views
  • 40. Marionette.ItemView Renders a single item (Backbone.Model) Backbone.Model ItemView
  • 41. Marionette.CollectionView Renders the items of a Backbone.Collection Doesn’t need a template CollectionView ItemView Backbone.Model Backbone.Collection ItemView Backbone.Model
  • 42. 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
  • 44. Before going further, choose your template engine
  • 45. Underscore templates works out of the box <script type="template" id="post-template"> <h2> <%- title %> </h2> </script>
  • 46. 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
  • 47. Using Rails? Go with Jade + JST gem 'tilt-jade' Compiles jade templates into js functions for use as clientside templates
  • 48. Jade is just amazing .post-content header(class='ng-wtf') h1= title span by #{author} ! if youAreUsingJade p You are amazing ! .body= description
  • 49. Backbone.Marionette.Renderer.render = (tpl, data) -> path = JST["apps/" + tpl] throw "Template #{tpl} not found!" unless path path data CoffeeScript...
  • 50. back to our app let's implement these views
  • 51. 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
  • 54. 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>
  • 55. 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
  • 56. 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
  • 58. 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()
  • 59. 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!"; } }, ! ... });
  • 60. 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 () { /* ... */ } ! });
  • 61. 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);
  • 62. Marionette in the real world — 5 minutes of Marionette applied to Cronycle —
  • 63. header-region with ItemView (User, Backbone.Model) main-region with CollectionView (Backbone.Collection) CompositeView (Model + Collection) ItemView (Model) ItemView (Model)
  • 65. Modal windows, just an overlay region
  • 66. 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!");
  • 67. Fetching for new articles
  • 68. 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); } });
  • 69. 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 } ! });
  • 70. 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); } } } ! });
  • 71. put a test on it https://github.com/bradphelan/jasminerice + jasmine-rice for Rails users If you like your goat...
  • 72. 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'); ! }); });
  • 73. 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