Upload
trek-glowacki
View
408
Download
0
Embed Size (px)
DESCRIPTION
Chicago Ember.js Meetup, December 2012
Citation preview
applications: a series of statesa talk by @trek
<a href=”serialize/user/intent.fmt”>
GET https://simple.com/activity/transactions/3a709ef6-c300-43b4-bca0-af72d1ecd4ba
GET https://simple.com/activity/transactions/3a709ef6-c300-43b4-bca0-af72d1ecd4ba/edit
PUT https://simple.com/activity/transactions/3a709ef6-c300-43b4-bca0-af72d1ecd4ba
HTTP 302 Found
GET https://simple.com/activity
ͿԤԥԦԧԨԩԪԫԬԭԮԯՠֈ֍֎֏ࢪࢩࢨࢧࢦࢥࢤࢣࢢࢡࢠࡪࡩࡨࡧࡦࡥࡤࡣࡢࡡࡠ࡞࡛࡚࡙ࡘࡗࡖࡕࡔࡓࡒࡑࡐࡏࡎࡍࡌࡋࡊࡉࡈࡇࡆࡅࡄࡃࡂࡁࡀ࠾࠽࠼࠻࠺࠹࠸࠷࠶࠵࠴࠳࠲࠱࠰࠭ࠬࠫࠪࠩࠨࠧࠦࠥࠤࠣࠢࠡࠠࠟࠞࠝࠜࠛࠚ࠙࠘ࠗࠖࠕࠔࠓࠒࠑࠐࠏࠎࠍࠌࠋࠊࠉࠈࠇࠆࠅࠄࠃࠂࠁࠀ߿߾߽ٟؠׯ
http://imgs.xkcd.com/comics/regular_expressions.png
$('.some-selector').click(function(){ $.ajax({ success: function(response){ var html = $templates.transactionDetails(response); $('#some-section-of-my-page).html(html); } })})
$('.some-selector').click(function(){ $.ajax({ success: function(response){ var html = $templates.transactionDetails(response); $('#some-section-of-my-page).html(html); } })})
$('.some-button').click(function(){ $.ajax({ type: ‘post’, success: function(response){ var html = $templates.transactionDetails(response); $('#some-section-of-my-page).html(html); } })})
decent, not great.
{}
truth-in-dom
JSON, js, html
JSON, js, html
{ vendor: 'Wholefds Kbs', amount: '20.70', isCredit: false, isPending: true, type: 'Groceries', location: { lat: '41.910006', long: '87.657166', address: '1070 N North Branch St,\n Chicago IL 60642' }}
success: function(purchase){ var sidebar = $('#more-info'); if(purchase.isPending) { $('.is-pending', sidebar).show(); } $('.name', sidebar).html(purchase.vendor); $('.amount', sidebar).html('-' + purchase.amount); $('.category', sidebar).html(purchase.type); $('.map', sidebar).gMapPlugin(purchase.location); $('.address', sidebar).html(purchase.location.address);}
success: function(purchase){ var sidebar = $('#more-info'), template = Templates.purchase; sidebar.html(template(purchase)); $('.map', sidebar).gMapPlugin(purchase.location);}
truth-in-dom
JSON, js, html
JSON, js, html
truth-in-dom
JSON, js, html
truth-in-domJSON, js,
html
truth-in-dom
success: function(purchase){ var sidebar = $('#more-info'), listItem = $(‘#list .purchase-’ + purchase.id), purchaseTemplate = Templates.purchase.show, purchaseTableRowTemplate = Templates.purchase.row; sidebar.html(purchaseTemplate(purchase)); listItem.html(purchaseTableRowTemplate(purchase)); $('.map', sidebar).gMapPlugin(purchase.location);}
<div id=”purchase-list”></div>
View
Collection of Models
View
View
View
View
View
View
View
Properties of the collection
<div id=”details”></div>
View
Aggregation of Collection
Different View
Single Model
app.Purchase = Backbone.Model.extend();
app.PurchaseList = Backbone.Collection.extend({ model: app.Purchase});app.PurchaseList.url = ‘purchases’
app.Purchases = new app.PurchaseList;
app.PurchaseListView = Backbone.View.extend({el: ‘#purchase-list’,
initialize: function(){ this.collection = app.Purchases; this.collection.on('change', this.render, this) this.render(); }, render: function(){
this.$el.append(new PurchasesFilterView().render()); _.each(this.collection.models, function (item) { this.$el.append(new PurchaseRowView({model: item})
.render()); }, this); }});
app.PurchaseRowView = Backbone.View.extend({ initialize: function() { this.model.on( 'change', this.render, this ); }, tagName: 'li', template: ..., events: {
'click': 'toggleMoreDetails', 'click .edit': 'toggleEdit'},
toggleMoreDetails: function(){ this.model.toggleMoreDetails(); this.$el.toggleClass( 'selected', this.moreDetailsShowing); }, render: function(){ this.$el.html(this.template(this.model)) }});
app.PurchaseDetailsView = Backbone.View.extend({el: ‘#details’,
initialize: function(){ this.render(); }, template: '...', render: function(){
this.$el.html(this.template(this.model); }});
app.Purchases.fetch();
<div id=”purchase-list”></div>
render: function(){this.$el.append(new PurchasesFilterView().render());
_.each(this.collection.models, function (item) { this.$el.append(new PurchaseRowView({model: item})
.render()); }, this); }
app.PurchaseRowView = Backbone.View.extend({...
events: { 'click': 'toggleMoreDetails' }, toggleMoreDetails: function(){ this.model.toggleMoreDetails(); this.$el.toggleClass( 'selected', this.moreDetailsShowing); }});
truth-in-data
truth-in-datatruth-in-data
model.on(‘change’)/collection.on(‘change’)
-> render
model.on(‘change’)-> render
model.on(‘change’)-> render
{}
<div id=”purchase-list”>
<div id=”details”></div>
<div id=”dashboard”>
</div>
<div id=”sidebar”>
</div>
<div id=”map”></div>
</div>
app.DashboardView = Backbone.View.extend({ render: function(){
this.$el.append(new app.PurchaesView().render().el);this.$el.append(new app.PurchaesMapView().render().el);this.$el.append(new app.PurcaseDetailView().render().el);
}});
http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
<div id=”purchase-list”>
<div id=”details”></div>
<div id=”dashboard”>
</div>
<div id=”sidebar”>
</div>
<div id=”map”></div>
</div>
possible, but you must be cautious
{{view App.NavigationView}}
{{view App.SummaryView}}
{{ outlet }}
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
App = Ember.Application.create();
App.ApplicationView = Ember.View.extend({ templateName: 'application'});
App.ApplicationController = Ember.Controller.extend();
App.Router = Ember.Route.extend({ root: Ember.Route.extend({}) // this happens for you: ‘shared instance’// applicationController: App.ApplicationController.create()
})
{{view App.NavigationView}}{{view App.SummaryView}}
{{outlet mainArea}}{{outlet detailsArea}}
application.handlebars
{{view App.NavigationView}}{{view App.SummaryView}}
{{outlet mainArea}}{{outlet detailsArea}}
application.handlebars
{{view App.NavigationView}}{{view App.SummaryView}}
{{outlet mainArea}}{{outlet detailsArea}}
application.handlebars
{{view App.NavigationView}}{{view App.SummaryView}}
{{outlet mainArea}}{{outlet detailsArea}}
application.handlebars
{{view App.NavigationView}}{{view App.SummaryView}}
{{outlet mainArea}}{{outlet detailsArea}}
application.handlebars
Purchases
Index Viewing Editing
App = Ember.Application.create();
App.ApplicationView = Ember.View.extend({ templateName: 'application'});
App.ApplicationController = Ember.Controller.extend();
App.Router = Ember.Route.extend({ root: Ember.Route.extend({})})
App = Ember.Application.create();
App.ApplicationView = Ember.View.extend({ templateName: 'application'});
App.ApplicationController = Ember.Controller.extend();
App.Router = Ember.Route.extend({ root: Ember.Route.extend({ purchases: Ember.Route.extend({ index: Ember.Route.extend({ }) }) })});
{{view App.NavigationView}}{{view App.SummaryView}}
{{outlet mainArea}}{{outlet detailsArea}}
application.handlebars
App = Ember.Application.create();
App.ApplicationView = Ember.View.extend({ templateName: 'application'});
App.ApplicationController = Ember.Controller.extend();
App.Router = Ember.Route.extend({ root: Ember.Route.extend({ purchases: Ember.Route.extend({ index: Ember.Route.extend({ }) }) })});
purchases: Ember.Route.extend({ index: Ember.Route.extend({ })})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ connectOutlets: function(router){
var controller = router.get('applicationController'), purchases = App.Purchase.find(), locations = purchases.get('locations'); controller.connectOutlet('mainArea', 'purchaseList', purchases); controller.connectOutlet('detailsArea', 'map', locations); } })})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ connectOutlets: function(router){
var controller = router.get('applicationController'), purchases = App.Purchase.find(), locations = purchases.get('locations'); controller.connectOutlet('mainArea', 'purchaseList', purchases); controller.connectOutlet('detailsArea', 'map', locations); } })})
where? what? data context?
controller.connectOutlet('mainArea', 'purchaseList', purchases);
controller.connectOutlet('mainArea', 'purchaseList', purchases);
App.PurchaseListView = Ember.View.extend({ templateName: 'purchaseList'});App.PurchaseListController = Ember.ArrayController.extend();
Ember.ArrayController.extend();
proxy/presenter/controller/thingie
proxy
content
proxy
content
what’s your length?
proxy
content
how are you sorted?
controller.connectOutlet('mainArea', 'purchaseList', purchases);
App.PurchaseListView = Ember.View.extend({ templateName: 'purchaseList'});App.PurchaseListController = Ember.ArrayController.extend();
controller.connectOutlet('mainArea', 'purchaseList', purchases);
App.PurchaseListView = Ember.View.extend({ templateName: 'purchaseList'});App.PurchaseListController = Ember.ArrayController.extend();
{{#each purchase in controller}} <li> {{purchase.date}} {{purchase.location}} {{purchase.amount}} </li>{{/each}}
controller.connectOutlet('mainArea', 'purchaseList', purchases);
App.PurchaseListView = Ember.View.extend({ templateName: 'purchaseList'});App.PurchaseListController = Ember.ArrayController.extend();
{{#each purchase in controller}} <li> {{purchase.date}} {{purchase.location}} {{purchase.amount}} </li>{{/each}}
purchases: Ember.Route.extend({ index: Ember.Route.extend({ connectOutlets: function(router){
var controller = router.get('applicationController'), purchases = App.Purchase.find(), locations = purchases.get('locations'); controller.connectOutlet('mainArea', 'purchaseList', purchases); controller.connectOutlet('detailsArea', 'map', locations); } })})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ connectOutlets: function(router){
var controller = router.get('applicationController'), purchases = App.Purchase.find(), locations = purchases.get('locations'); controller.connectOutlet('mainArea', 'purchaseList', purchases); controller.connectOutlet('detailsArea', 'map', locations); } })})
where? what? data context?
controller.connectOutlet('detailsArea', 'map', locations);
App.MapView = Ember.View.extend({ templateName: 'googleMap'});App.MapController = Ember.ArrayController.extend();
controller.connectOutlet('detailsArea', 'map', locations);
controller.connectOutlet('detailsArea', 'map', locations);
App.MapView = Ember.View.extend({ templateName: 'googleMap'});App.MapController = Ember.ArrayController.extend();
controller.connectOutlet('detailsArea', 'map', locations);
App.MapView = Ember.View.extend({ templateName: 'googleMap'});App.MapController = Ember.ArrayController.extend();
controller.connectOutlet('detailsArea', 'map', locations);
App.MapView = Ember.View.extend({ templateName: 'googleMap'});App.MapController = Ember.ArrayController.extend();
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
{{#each purchase in controller}} <li> {{purchase.date}} {{purchase.location}} {{purchase.amount}} </li>{{/each}}
{{#each purchase in controller}} <li {{action showDetails purchase}}> {{purchase.date}} {{purchase.location}} {{purchase.amount}} </li>{{/each}}
purchases: Ember.Route.extend({ index: Ember.Route.extend({ connectOutlets: function(router){
... } })})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } })})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }), viewing: Ember.Route.extend({ })
})
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }), viewing: Ember.Route.extend({ connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
where? what? data context?
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }), viewing: Ember.Route.extend({ connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }), viewing: Ember.Route.extend({ connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
{{#each purchase in controller}} <li {{action showDetails purchase}}> {{purchase.date}} {{purchase.location}} {{purchase.amount}} </li>{{/each}}
controller.connectOutlet('detailsArea', 'purchaseDetails', context);
controller.connectOutlet('detailsArea', 'purchaseDetails', context);
App.PurchaseDetailsView = Ember.View.extend({ templateName: 'details'});App.PurchaseDetailsController = Ember.ObjectController.extend();
controller.connectOutlet('detailsArea', 'purchaseDetails', context);
App.PurchaseDetailsView = Ember.View.extend({ templateName: 'details'});App.PurchaseDetailsController = Ember.ObjectController.extend();
<div class='actions'> <a {{action editPurchase content}}>
Edit</a>
<a href='...'>Support</a></div>
{{#if pending}} <h3>
This transaction is pending...</h3>
{{/if}}
{{name}}{{amount}}{{label}}
App.PurchaseDetailsView = Ember.View.extend({ templateName: 'details'});App.PurchaseDetailsController = Ember.ObjectController.extend();
App.PurchaseDetailsView = Ember.View.extend({ templateName: 'details'});App.PurchaseDetailsController = Ember.ObjectController.extend();
<div class='actions'> <a {{action editPurchase content}}>
Edit</a>
<a href='...'>Support</a></div>
{{#if pending}} <h3>
This transaction is pending...</h3>
{{/if}}
{{name}}{{amount}}{{label}}
Ember.ObjectController.extend();
proxy
content
Ember.ObjectController.extend();
proxy
content
are you pending?
App.PurchaseDetailsView = Ember.View.extend({ templateName: 'details'});App.PurchaseDetailsController = Ember.ObjectController.extend();
<div class='actions'> <a {{action editPurchase content}}>
Edit</a>
<a href='...'>Support</a></div>
{{#if pending}} <h3>
This transaction is pending...</h3>
{{/if}}
{{name}}{{amount}}{{label}}
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
<div class='actions'> <a {{action editPurchase content}}>
Edit</a>
<a href='...'>Support</a></div>
{{#if pending}} <h3>
This transaction is pending...</h3>
{{/if}}
{{name}}{{amount}}{{label}}
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }), viewing: Ember.Route.extend({ connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }), viewing: Ember.Route.extend({ editPurchase: Ember.Route.transitionTo(‘editing’), connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }),editing: Ember.Route.extend({ connectOutlets: function(router, context){
var controller = router.get('applicationController'); controller. connectOutlet('detailsArea', 'editPurchaseDetails', context); }}),
viewing: Ember.Route.extend({ editPurchase: Ember.Route.transitionTo(‘editing’), connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }),editing: Ember.Route.extend({ connectOutlets: function(router, context){
var controller = router.get('applicationController'); controller. connectOutlet('detailsArea', 'editPurchaseDetails', context); }}),
viewing: Ember.Route.extend({ editPurchase: Ember.Route.transitionTo(‘editing’), connectOutlets: function(router, context){ var controller = router.get('applicationController'); controller.
connectOutlet('detailsArea', 'purchaseDetails', context);}
})})
where? what? data context?
connectOutlet('detailsArea', 'editPurchaseDetails', context);
connectOutlet('detailsArea', 'editPurchaseDetails', context);
App.EditPurchaseDetailsView = Ember.View.extend({ templateName: 'edit-details'});App.EditPurchaseDetailsController = Ember.ObjectController.extend();
connectOutlet('detailsArea', 'editPurchaseDetails', context);
App.EditPurchaseDetailsView = Ember.View.extend({ templateName: 'edit-details'});App.EditPurchaseDetailsController = Ember.ObjectController.extend();
<a {{action save context}}>Save</a><a {{action cancel}}>Cancel</a>
{{view Ember.TextField valueBinding="name"}}
connectOutlet('detailsArea', 'editPurchaseDetails', context);
App.EditPurchaseDetailsView = Ember.View.extend({ templateName: 'edit-details'});App.EditPurchaseDetailsController = Ember.ObjectController.extend();
<a {{action save context}}>Save</a><a {{action cancel}}>Cancel</a>
{{view Ember.TextField valueBinding="name"}}
Ember.ObjectController.extend();
proxy
content
Ember.ObjectController.extend();
proxy
content
what’s your name?
Purchases
Index Viewing Editing
Purchases
Index Viewing Editing
purchases: Ember.Route.extend({ index: Ember.Route.extend({ showDetails: Ember.Route.transitionTo(‘viewing’), connectOutlets: function(router){
... } }),editing: Ember.Route.extend({ saveChanges: Ember.Route.transitionTo(‘index’), connectOutlets: function(router, context){
... }}),
viewing: Ember.Route.extend({ editPurchase: Ember.Route.transitionTo(‘editing’), connectOutlets: function(router, context){
... })})
Purchases
Index Viewing Editing
Demeter’d
> 7 views
which pattern, when?
• app is just a series of documents• or you’re just coding single page• not a client app• manipulation mostly presentational• few data communications• user interaction brief, simple, infrequent
• app is series of documents• with “islands of richness”• occassional data communications• multiple parts of a page need to reflect data• shallow view hierarchy (1-2 levels)• small number of views (~7)• user interaction brief and/or infrequent
• frequent data communications• many parts of a page need to reflect data• deep view hierarchy (2-3+)• large number of views• user will remain for large amounts of time• and/or frequently return• server is just an api• you’d *almost* write a desktop/iOS app
Mobile Cocoa TouchAndroid SDK
Desktop Cocoa.NET
Web ?
Mobile Cocoa TouchAndroid SDK
Desktop Cocoa.NET
Web
User Interface HTML+CSS
Data Persistence
Application Architecture
fin