Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
The Rendering Revolution: Running Apps in the B...
Search
Trek Glowacki
October 24, 2013
Technology
2
430
The Rendering Revolution: Running Apps in the Browser with Ember.js
Trek Glowacki
October 24, 2013
Tweet
Share
More Decks by Trek Glowacki
See All by Trek Glowacki
Documents are not the web
trek
1
210
Beyond Front-end Developer
trek
1
460
UI Patterns and Reuse ~ or ~ Why I Hope We Never Need an Ember.js Widget Library
trek
10
5.9k
applications: a series of states
trek
19
2k
Cargo Cults
trek
0
320
Other Decks in Technology
See All in Technology
ビズリーチが挑む メトリクスを活用した技術的負債の解消 / dev-productivity-con2025
visional_engineering_and_design
3
6.9k
20250705 Headlamp: 專注可擴展性的 Kubernetes 用戶界面
pichuang
0
250
SmartNewsにおける 1000+ノード規模 K8s基盤 でのコスト最適化 – Spot・Gravitonの大規模導入への挑戦
vsanna2
0
120
使いたいMCPサーバーはWeb APIをラップして自分で作る #QiitaBash
bengo4com
0
1.6k
AI時代の開発生産性を加速させるアーキテクチャ設計
plaidtech
PRO
3
130
改めてAWS WAFを振り返る~業務で使うためのポイント~
masakiokuda
2
250
Delegating the chores of authenticating users to Keycloak
ahus1
0
140
MUITにおける開発プロセスモダナイズの取り組みと開発生産性可視化の取り組みについて / Modernize the Development Process and Visualize Development Productivity at MUIT
muit
1
15k
MobileActOsaka_250704.pdf
akaitadaaki
0
110
いつの間にか入れ替わってる!?新しいAWS Security Hubとは?
cmusudakeisuke
0
100
自律的なスケーリング手法FASTにおけるVPoEとしてのアカウンタビリティ / dev-productivity-con-2025
yoshikiiida
1
15k
ドメイン特化なCLIPモデルとデータセットの紹介
tattaka
2
580
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
5.9k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
48
2.9k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
950
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Navigating Team Friction
lara
187
15k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
7
730
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.3k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Speed Design
sergeychernyshev
32
1k
Practical Orchestrator
shlominoach
189
11k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
Transcript
@trek
None
None
None
IE 5 IE 8 IE 7
None
None
None
None
None
None
None
None
None
2003 2005 2009
None
None
None
None
None
None
Why?
None
None
None
None
None
None
None
screen rendering display screen state AFK behavior persistence
screen rendering display screen state AFK behavior persistence client server
screen rendering display screen state AFK behavior persistence client server
APIs Node, Rails
APIs
screen rendering display screen state AFK behavior persistence client server
screen rendering display screen state AFK behavior persistence client server
None
Mobile Cocoa Touch Android SDK Desktop Cocoa .NET Web ?
Mobile Cocoa Touch Android SDK Desktop Cocoa .NET Web
None
None
Router Route Controller View/Template Model/Data
Router Route Controller View/Template Model/Data what is the current context?
how does this encode to a URL?
Router Route Controller View/Template Model/Data What data should I fetch?
What template should I show?
Router Route Controller View/Template Model/Data how do I translate user
interaction in this context into intent (e.g. a "click" into "show me more")? how do I present the data for this context?
Router Route Controller View/Template Model/Data how do I paint to
the screen?
Router Route Controller View/Template Model/Data what is the structure of
my data
Router Route Controller View/Template Model/Data
App = Ember.Application.create(); <!-- application.hbs --> <h1>Welcome to Ember.js</h1> Router
Route Controller View/Template Model/Data
None
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); <!-- application.hbs --> <h1>Welcome to Ember.js</h1> <nav> {{#link-to 'about'}}About{{/link-to}} {{#link-to 'favorites'}}Favorites{{/link-to}} {{outlet}}<!-- filled with child template when you navigate into route --> </nav> <!-- favorites.hbs --> <h2>Favorites<h2> <ul> <li>...</li> <li>...</li> </ul> <!-- about.hbs --> <h2>About<h2>
None
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); <!-- application.hbs --> <h1>Welcome to Ember.js</h1> <nav> {{#link-to 'about'}}About{{/link-to}} {{#link-to 'favorites'}}Favorites{{/link-to}} {{outlet}}<!-- filled with child template when you navigate into route --> </nav> <!-- favorites.hbs --> <h2>Favorites<h2> <ul> <li>...</li> <li>...</li> </ul> <!-- about.hbs --> <h2>About<h2>
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); App.AboutRoute = Ember.Route.extend({ model: function(){ return $.ajax({ url: '/api/currentUser/profile' }); } });
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); App.AboutRoute = Ember.Route.extend({ model: function(){ var self = this; $('#a-loader).show(); $.ajax({ url: '/api/currentUser/profile', success: function(response){ $('#a-loader).hide(); self.renderWithSomeData(response); } }); } });
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); App.AboutRoute = Ember.Route.extend({ model: function(){ return $.ajax({ url: '/api/currentUser/profile' }); } }); { name: 'trek', bio: 'Much shorter than you expect' }
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); <!-- favorites.hbs --> <h2>Favorites<h2> <ul> <li>...</li> <li>...</li> </ul> <!-- about.hbs --> <h2>About<h2> <!-- application.hbs --> <h1>Welcome to Ember.js</h1> <nav> {{#link-to 'about'}}About{{/link-to}} {{#link-to 'favorites'}}Favorites{{/link-to}} {{outlet}}<!-- filled with child template when you navigate into route --> </nav>
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); <!-- favorites.hbs --> <h2>Favorites<h2> <ul> <li>...</li> <li>...</li> </ul> <!-- about.hbs --> <h1>About<h1> <h2>{{name}}<h2> <div class='bio'> {{bio}} </div> <!-- application.hbs --> <h1>Welcome to Ember.js</h1> <nav> {{#link-to 'about'}}About{{/link-to}} {{#link-to 'favorites'}}Favorites{{/link-to}} {{outlet}}<!-- filled with child template when you navigate into route --> </nav>
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); App.FavoritesRoute = Ember.Route.extend({ model: function(){ return $.ajax({ url: '/api/currentUser/favorites' }); } }); [ {"id": 1, "title": "Hoverbike Captains of the SS"}, {"id": 2, "title": "The Terrifying Madness That Lies Beyond"} ]
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); <!-- favorites.hbs --> <h2>Favorites<h2> <ul> <li>...</li> <li>...</li> </ul> <!-- about.hbs --> <h1>About<h1> <h2>{{name}}<h2> <div class='bio'> {{bio}} </div> <!-- application.hbs --> <h1>Welcome to Ember.js</h1> <nav> {{#link-to 'about'}}About{{/link-to}} {{#link-to 'favorites'}}Favorites{{/link-to}} {{outlet}}<!-- filled with child template when you navigate into route --> </nav>
App = Ember.Application.create(); App.Router.map(function(){ this.route("about"); this.route("favorites", { path: "/favs" });
}); <!-- favorites.hbs --> <h2>Favorites<h2> <ul> {{#each}} <li> {{title}} </li> {{/each}} </ul> <!-- about.hbs --> <h1>About<h1> <h2>{{name}}<h2> <div class='bio'> {{bio}} </div> <!-- application.hbs --> <h1>Welcome to Ember.js</h1> <nav> {{#link-to 'about'}}About{{/link-to}} {{#link-to 'favorites'}}Favorites{{/link-to}} {{outlet}}<!-- filled with child template when you navigate into route --> </nav>
None
None
None
var Rdio = Ember.Application.create(); Rdio.Router.map(function(){}); Rdio.ApplicationRoute = Ember.Route.extend({ model: function(){
return $.ajax({ url: "/api/authenticate" }) } }); <!-- application.hbs --> {{outlet}} <!-- loading.hbs --> <div class="loading-container"> <span class="icon-loading spinner"/> </div>
None
{ username: 'trek', messageCount: 4, playlists: [ {id: 981, title:
"Everyday I'm shufflin'"}, {id: 191, title: "RUNNIN"} ] }
None
<div class='navigation'> <ul> <h3>Browse</h3> <li>{{#link-to "heavyRotation"}}Heavy Rotation{{/link-to}}</li> <li>{{#link-to "recentActivity"}}Recent Activity{{/link-to}}</li>
<li>{{#link-to "topCharts"}}Top Charts{{/link-to}}</li> <li>{{#link-to "newReleases"}}New Releases{{/link-to}}</li> <li>{{#link-to "stations"}}Stations{{/link-to}}</li> </ul>
<ul> <h3>Playlists</h3> <ul> {{#each playlists}} {{#link-to "playlist" this}} {{name}} {{/link-to}}
{{/each}} </ul> </ul>
<ul> <h3>Playlists</h3> <ul> {{#each playlists}} {{#link-to "playlist" this}} {{name}} {{/link-to}}
{{/each}} </ul> </ul> { username: 'trek', messageCount: 4, playlists: [ {id: 981, title: "Everyday I'm shufflin'"}, {id: 191, title: "RUNNIN"} ] }
<div class='top-bar'> <ul> <li>{{#link-to "profile"}}{{username}}{{/link-to}}</li> <li>{{#link-to "alets"}}{{messageCount}}{{/link-to}}</li> </ul>
<div class='top-bar'> <ul> <li>{{#link-to "profile"}}{{username}}{{/link-to}}</li> <li>{{#link-to "alets"}}{{messageCount}}{{/link-to}}</li> </ul> { username:
'trek', messageCount: 4, playlists: [ {id: 981, title: " {id: 191, title: " ] }
{{outlet}}
None
None
var Rdio = Ember.Application.create(); Rdio.Router.map(function(){}); Rdio.ApplicationRoute = Ember.Route.extend({ model: function(){
return $.ajax({ url: "/api/authenticate" }) } });
var Rdio = Ember.Application.create(); Rdio.Router.map(function(){ this.route("heavyRotation") }); Rdio.ApplicationRoute = Ember.Route.extend({
model: function(){ return $.ajax({ url: "/api/authenticate" }) } }); Rdio.HeavyRotationRoute = Ember.Route.extend({ model: function(){ return $.ajax({ url: "/api/heavyRotation" }) } });
None
[ { title: "The Speed of Things", artistName: "Dale Earnhardt
Jr. Jr.", songsCount: 13, isExplicit: false, imageUrl: "/images/c10efa31.png" }, { title: "I Love You", artistName: "The Neighbourhood", songsCount: 11, isExplicit: true, imageUrl: "/images/9ec1ce1.png" } {...}, {...} ]
None
<!-- heavyRotation.hbs --> <h3>Heavy Rotation</h3> {{#each}} {{link-to "album" this}} <div
class="album-"> </div> {{title}} {{artistName}} {{songsCount}} songs {{#if isExplicit}} EXPLICIT {{/if}} {{/link-to}} {{/each}}
<!-- heavyRotation.hbs --> <h3>Heavy Rotation</h3> {{#each}} {{link-to "album" this}} <div
class="album-"> </div> {{title}} {{artistName}} {{songsCount}} songs {{#if isExplicit}} EXPLICIT {{/if}} {{/link-to}} {{/each}} [ { title: "I Love You", artistName: "The Neighbourhood", songsCount: 11, isExplicit: true, imageUrl: "/images/9ec1ce1.png" } ]
None
None
None
Rdio.Router.map(function(){ this.route("heavyRotation"); this.route("album", {path: "album/:album_id"}); }); Rdio.AlbumRoute = Ember.Route.extend({ model:
function(params){ return $.ajax({ url: "/api/album/" + params.album_id }); } })
None
{ title: "The Speed of Things", artistName: "Dale Earnhardt Jr.
Jr.", songsCount: 13, isExplicit: false, imageUrl: "/images/c10efa31.png", playCount: 32694, songs: [ {trackNumber: 1, title: "Beautiful Dream"}, {trackNumber: 2, title: "Run"} ] }
None
None
None
None
None
Rdio.Router.map(function(){ this.resource("artist", {path: "artist/:artist_id"}, function(){ this.route("albums", {path: '/'}); this.route("songs"); })
}); Rdio.ArtistRoute = Ember.Route.extend({ model: function(params){ return $.ajax({ url: "/api/artist/" + params.artist_id }); } }); Rdio.ArtistAlbumsRoute = Ember.Route.extend({ model: function(parmas, transition){ return $.ajax({ url: "/api/artist/" + transition.params.artist_id + "/albums" }); } });
Rdio.Router.map(function(){ this.resource("artist", {path: "artist/:artist_id"}, function(){ this.route("albums", {path: '/'}); this.route("songs"); })
}); Rdio.ArtistRoute = Ember.Route.extend({ model: function(params){ return $.ajax({ url: "/api/artist/" + params.artist_id }); } }); Rdio.ArtistSongsRoute = Ember.Route.extend({ model: function(parmas, transition){ return $.ajax({ url: "/api/artist/" + transition.params.artist_id + "/songs" }); } });
None
None
None
None
None
None
None
None
None
None
None
<!-- application.hbs --> {{outlet}} {{outlet "playbar"}}
<!-- application.hbs --> {{outlet}} {{outlet "playbar"}}
Rdio.ApplicationRoute = Ember.Route.extend({ model: function(){ return $.ajax({ url: "/api/authenticate" })
} });
Rdio.ApplicationRoute = Ember.Route.extend({ model: function(){ return $.ajax({ url: "/api/authenticate" })
}, renderTemplate: function(){ this._super(); this.render('playbar', { into: 'application', outlet: 'playbar' }); } });
None
None
{{#each tracks}} <ul> <li> {{trackNumber}} {{name}} </li> </ul> {{/each}}
{{#each tracks}} <ul> <li {{action "play" this}}> {{trackNumber}} {{name}} </li>
</ul> {{/each}}
current controller current route {{#each tracks}} <ul> <li {{action "play"
this}}> {{trackNumber}} {{name}} </li> </ul> {{/each}} parent route application route ...
Rdio.ApplicationRoute = Ember.Route.extend({ actions: { play: function(song){ this.controllerFor('playbar').send('play', song); }
} });
Rdio.ApplicationRoute = Ember.Route.extend({ actions: { play: function(song){ this.controllerFor('playbar').send('play', song); }
} }); Rdio.PlaybarController = Ember.Controller.extend({ actions: { play: function(song){ ... } } });
None