Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ember: when the lights go on
Search
Aaron Chambers
September 24, 2014
Programming
210
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ember: when the lights go on
My BSkyB Engineering Offsite presentation about our journey with Ember and beyond
Aaron Chambers
September 24, 2014
More Decks by Aaron Chambers
See All by Aaron Chambers
{{should-avoid this.renderModifiers butHow=true}}
achambers
0
200
one command to deploy them all
achambers
0
84
The Legend of Zelda: A {{link-to}} the past
achambers
0
95
ember-cli-deploy
achambers
0
97
git-branching
achambers
0
110
Other Decks in Programming
See All in Programming
Agents on Rails - Rails at Scale 2026
irinanazarova
0
560
すこし踏み込む CancellationToken
htkym
2
1.7k
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
220
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
190
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
210
全ての永続トークンを、生まれる前に消し去りたい!
sylph01
2
900
残高管理から台帳サービスへの進化
artoy
1
200
R8 の設定見直しでアプリサイズ削減
andpad
0
220
Agentic Software Factoryに、すごく賢いIF文を。 / super smart IF statement into the Agentic Software Factory.
rkaga
8
2.2k
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
610
IBM Bob でAI駆動開発『QC-Playbook』
ichirokano2
0
110
Vue Fes Japan の “作る側”に関わってみて
naokihaba
0
160
Featured
See All Featured
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
Paper Plane
katiecoart
PRO
5
54k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
How GitHub (no longer) Works
holman
316
150k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
990
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
430
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.2k
New Earth Scene 8
popppiees
4
2.7k
Crafting Experiences
bethany
1
380
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.9k
Code Review Best Practice
trishagee
74
20k
Transcript
ember:&when&the&lights&go&on @grandazz github.com/achambers
None
how$ember$saved$my$life @grandazz github.com/achambers
how$ember$saved$my$life @grandazz github.com/achambers
ember:&when&the&lights&go&on @grandazz github.com/achambers
Wenguins
None
Noah%update
None
this%is%not an#ember#is#be(er#than#everything#else presenta(on
browser'dev'boiled'down (shamelessley)borrowed)from)@ryanflorence) interac(ng*with*data*stores rendering'data'to'ui responding*to*user*interac/on router&&&urls project(stuff:(file(organisa3on,(build(tools(etc
None
our$journey$with ember
lightbulb!moments
we#didn't#just#jump#in
we#needed#to validate the$switch
None
None
Service'Status
None
// app/assets/javascripts/application.js function bindViewMoreDetailsLinks() { $('.more-details').hide(); $('.view-more').click(function(e) { e.preventDefault(); var
viewMoreLink = $(this); var moreDetails = viewMoreLink.parents('.issue').find('.more-details'); if (moreDetails.is(':visible')) { moreDetails.hide(); viewMoreLink.text('More details'); } else { moreDetails.show(); viewMoreLink.text('Less details'); } viewMoreLink.toggleClass('opened'); }); }
// app/assets/javascripts/application.js function toggleElementsForEditors() { $.ajax({ url: '/sessions/current', dataType: 'json',
success: function(data) { var loggedInStatus = data.logged_in; toggleNewIssueLink(loggedInStatus); toggleHeader(loggedInStatus); toggleAdditionalLinks(loggedInStatus); toggleSidebar(loggedInStatus); toggleFooter(loggedInStatus); toggleEditIssueLinks(loggedInStatus); toggleResolveIssueLinks(loggedInStatus); } }); }
// app/assets/javascripts/application.js function toggleHeader(loggedInStatus) { $('#skycom-masthead-wrapper').toggleClass('hidden', loggedInStatus); } function toggleAdditionalLinks(loggedInStatus)
{ $('.additional-links').toggleClass('hidden', loggedInStatus); } function toggleSidebar(loggedInStatus) { $('.sidebar').toggleClass('hidden', loggedInStatus); } function toggleFooter(loggedInStatus) { $('#skycom-footer-wrapper').toggleClass('hidden', loggedInStatus); } function toggleEditIssueLinks(loggedInStatus) { $('.btn.edit-issue').toggleClass('hidden', !loggedInStatus); } function toggleResolveIssueLinks(loggedInStatus) { $('.btn.resolve-issue').toggleClass('hidden', !loggedInStatus); }
None
data$based$ui$manipula.on
// app/views/issues/index.html.haml function microfilterCheck() { var deferred = $.Deferred(); $.getJSON('/sessions/current',
function(json) { if (json.logged_in) { deferred.resolve() } else { Hub.runMicrofilterTest() .done(function() { deferred.reject(); }) .fail(function() { deferred.resolve(); }); } }); return deferred; }
// app/views/issues/index.html.haml function restrictionCheck() { var deferred = $.Deferred(); $.getJSON('/customers/current',
function(json) { if (json.customer.has_debt_restriction) { deferred.reject('debt'); } else if (json.customer.has_high_spend_restriction) { deferred.reject('spend'); } else { deferred.resolve(); } }, function() { deferred.resolve(); }); return deferred; }
// app/views/issues/index.html.haml $(function() { var microfilterDeferred = microfilterCheck() var restrictionDeferred
= restrictionCheck() $.when(microfilterDeferred, restrictionDeferred).always(function() { var args = Array.prototype.slice.call(arguments); if (restrictionDeferred.state() == 'rejected') { if (args.indexOf('spend') !== -1) { $('.high-spend-restriction-alert').removeClass('hidden'); } else if (args.indexOf('debt') !== -1) { $('.debt-restriction-alert').removeClass('hidden'); } } else if (microfilterDeferred.state() == 'rejected') { $('.microfilter-alert').removeClass('hidden'); } showIssues(); }); });
then%the%very%next%commit...
// app/views/issues/index.html.haml $(function() { IssuesIndexController.init(); });
None
controllers
// app/assets/javasctipts/User.js var User = { isAnEditor: function() { var
deferred = $.Deferred(); $.ajax({ dataType: 'json', url: '/sessions/current', success: function(data) { deferred.resolve(data.logged_in); }, error: function() { deferred.reject(); } }); return deferred.promise(); } }
// app/assets/javascripts/Diagnostics.js var Diagnostics = { run: function() { //run
some diagnostics goodness } } // app/assets/javascripts/CBS.js var CBS = { run: function() { //run some monstrosity of a service, //return a massive payload we don't need //and take forever to do so } }
None
models & interac(ng*with*data*stores
// app/assets/javascripts/views/IssueView.js (function() { window.IssuesView = { render: function() {
this._showIssues(); this._toggleStatusMessage(); } // ...snip... }; })(); // app/assets/javascripts/views/DebtAlertView.js (function() { window.DebtAlertView = { render: function() { this._showAlert(); this._hideProducts(); this._hideSidebar(); this._informScreenReader(); } // ...snip... }; })();
None
views
{!-- app/assets/javascripts/templates/outages.hbs --}} {{#each outages}} <article class="issue"> <h3>{{description}}</h3> <p> <span
class="label">Date issue was reported</span> </br> <strong>{{startedOn}}</strong> </p> </article> {{/each}}
None
templa'ng
we#started#to#build#our#own#framework
we#started#to#build#our#own#framework BADLY
Use$a$framework,$or$don't.$Either$ way,$you$will. —"Someone"Awesome
conven&on'over'configura&on this.resource('posts', { path: '/posts' }) PostsRoute PostsController PostsView posts.hbs
no#more#globals var Diagnostics = { run: function() { // do
stuff } }; window.IssuesIndexController = { init: function() { // do stuff; } };
no#more#globals ServiceStatus.GoIssuesController = Em.ArrayController.extend({ status: function() { //do stuff }.property('hasProblem')
}); ServiceStatus.Outage = DS.Model.extend({ //attributes here }); ServiceStatus.SsAccordionComponent = Em.Component.extend({ collapsed: false, //other properties here });
data$binding // models/tv_issue.js ServiceStatus.TvIssue = DS.Model.extend( name: DS.attr('string') ); //
controllers.tv_issues.js ServiceStatus.TvIssuesController = Em.ArrayController.extend({ }); <!-- templates/tv_issues.hbs --> <ul> <#each> <li>{{name}}</li> </each> </ul>
lightbulb(moments didn't&stop there
None
None
leave%your%assump-ons at#the#door
None
None
None
None
None
None
None
don't&fight&the&framework
If#something#feels#harder#than#it# should#be,#it#probably#is.#You're# probably#doing#it#wrong. —"Someone"Awesome
None
None
ui#error#logging
ui#error#logging Ember.onerror = handleError; Ember.RSVP.on('error', handleError); function handleError(error) { Em.$.ajax('/error',
{ type: 'POST', data: { stack: error.stack } }); }
None
None
build&tools
ember%cli npm install -g ember-cli
ember%cli file$organisa,on ember new my-project - /app - /components -
/controllers - /helpers - /models - /routes - /styles - /templates - /views - app.js - index.html - router.js - /config - /public - /tests
ember%cli es6$modules
ember%cli tes$ng
ember%cli asset%pipeline
ember%cli generators
ember%cli h"p$mock)server
ember%cli addons
None
None
separa&on)of ui#and#api
clearer&separa)on&between&data and$user$interac,on
think¬&of&your&api&in&terms&of&ui& interac2ons,&but&in&terms&of&data& and&rela2onships
your%app%is%a client'of'your'api
None
None
deployment
deploying*to*heroku and$serving$from$rails is#cool#and#all
but$we've$been$doing$that$for a"long"'me
we#can#do be#er
None
None
index.html app#1234.js app#5678.css
None
lets%push%this one$step$further
None
None
deployment*becomes*super*quick
just%push%some%sta,c%assets%to%S3 and$push$index.html$into$redis
not$to$men'on$cu*ng$over
None
None
pact%file%tes*ng
how$do$we$test$that$changes to#the#api
how$do$we$test$that$changes to#the#api don't&break&the&ui?
pact%(noun) A%formal%agreement%between% individuals%or%par8es. —%The%Google
the$rela(onship$of$the$web$app$consists$of: the$consumer$(ui) the$provider$(api)
Step%1%'%Define%the%consumer%expecta4ons
Step%2%'%Verify%expecta0ons%on%provider
imagine'if CBS verified'their'services'against'our'pact'files every%&me%they%released?
None
Thats&a&wrap
@grandazz github.com/achambers
any$ques)ons? @grandazz github.com/achambers
thankyou