Applications: A Series of States

434 views

Published on

Chicago Ember.js Meetup, December 2012

Published in: Technology, Business
  • Be the first to comment

  • Be the first to like this

Applications: A Series of States

  1. 1. applications: a series of statesa talk by @trek
  2. 2. <a href=”serialize/user/intent.fmt”>
  3. 3. GET https://simple.com/activity
  4. 4. GET https://simple.com/activity/transactions/3a709ef6-c300-43b4-bca0-af72d1ecd4ba
  5. 5. GET https://simple.com/activity/transactions/3a709ef6-c300-43b4-bca0-af72d1ecd4ba/edit
  6. 6. PUT https://simple.com/activity/transactions/3a709ef6-c300-43b4-bca0-af72d1ecd4baHTTP 302 FoundGET https://simple.com/activity
  7. 7. !
  8. 8. http://imgs.xkcd.com/comics/regular_expressions.png
  9. 9. GET https://simple.com/activity
  10. 10. $(.some-selector).click(function(){$.ajax({success: function(response){var html = $templates.transactionDetails(response);$(#some-section-of-my-page).html(html);}})})
  11. 11. $(.some-selector).click(function(){$.ajax({success: function(response){var html = $templates.transactionDetails(response);$(#some-section-of-my-page).html(html);}})})
  12. 12. $(.some-button).click(function(){$.ajax({type: ‘post’,success: function(response){var html = $templates.transactionDetails(response);$(#some-section-of-my-page).html(html);}})})
  13. 13. decent, not great.
  14. 14. {}
  15. 15. truth-in-domJSON, js,htmlJSON, js,html
  16. 16. {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,nChicago IL 60642}}
  17. 17. 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);}
  18. 18. success: function(purchase){var sidebar = $(#more-info), template = Templates.purchase;sidebar.html(template(purchase));$(.map, sidebar).gMapPlugin(purchase.location);}
  19. 19. truth-in-domJSON, js,htmlJSON, js,html
  20. 20. truth-in-domJSON, js,htmltruth-in-domJSON, js,htmltruth-in-dom
  21. 21. 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);}
  22. 22. <div id=”purchase-list”></div>
  23. 23. View
  24. 24. Collection of Models
  25. 25. ViewViewViewViewViewViewView
  26. 26. Properties of the collection
  27. 27. <div id=”details”></div>
  28. 28. View
  29. 29. Aggregation ofCollection
  30. 30. Different View
  31. 31. Single Model
  32. 32. GET https://simple.com/activity
  33. 33. app.Purchase = Backbone.Model.extend();app.PurchaseList = Backbone.Collection.extend({model: app.Purchase});app.PurchaseList.url = ‘purchases’app.Purchases = new app.PurchaseList;
  34. 34. 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);}});
  35. 35. 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))}});
  36. 36. app.PurchaseDetailsView = Backbone.View.extend({el: ‘#details’,initialize: function(){this.render();},template: ...,render: function(){this.$el.html(this.template(this.model);}});
  37. 37. app.Purchases.fetch();
  38. 38. <div id=”purchase-list”></div>
  39. 39. render: function(){this.$el.append(new PurchasesFilterView().render());_.each(this.collection.models, function (item) {this.$el.append(new PurchaseRowView({model: item}).render());}, this);}
  40. 40. app.PurchaseRowView = Backbone.View.extend({...events: { click: toggleMoreDetails },toggleMoreDetails: function(){this.model.toggleMoreDetails();this.$el.toggleClass( selected, this.moreDetailsShowing);}});
  41. 41. truth-in-datatruth-in-datatruth-in-data
  42. 42. model.on(‘change’)/collection.on(‘change’)-> rendermodel.on(‘change’)-> rendermodel.on(‘change’)-> render
  43. 43. {}
  44. 44. <div id=”purchase-list”><div id=”details”></div><div id=”dashboard”></div><div id=”sidebar”></div><div id=”map”></div></div>
  45. 45. 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);}});
  46. 46. http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
  47. 47. <div id=”purchase-list”><div id=”details”></div><div id=”dashboard”></div><div id=”sidebar”></div><div id=”map”></div></div>
  48. 48. possible, but you must be cautious
  49. 49. {{view App.NavigationView}}{{view App.SummaryView}}
  50. 50. {{ outlet }}
  51. 51. PurchasesIndex Viewing Editing
  52. 52. PurchasesIndex Viewing Editing
  53. 53. PurchasesIndex Viewing Editing
  54. 54. PurchasesIndex Viewing Editing
  55. 55. PurchasesIndex Viewing Editing
  56. 56. PurchasesIndex Viewing Editing
  57. 57. PurchasesIndex Viewing Editing
  58. 58. PurchasesIndex Viewing Editing
  59. 59. PurchasesIndex Viewing Editing
  60. 60. PurchasesIndex Viewing Editing
  61. 61. PurchasesIndex Viewing Editing
  62. 62. 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()})
  63. 63. {{view App.NavigationView}}{{view App.SummaryView}}{{outlet mainArea}}{{outlet detailsArea}}application.handlebars
  64. 64. {{view App.NavigationView}}{{view App.SummaryView}}{{outlet mainArea}}{{outlet detailsArea}}application.handlebars
  65. 65. {{view App.NavigationView}}{{view App.SummaryView}}{{outlet mainArea}}{{outlet detailsArea}}application.handlebars
  66. 66. {{view App.NavigationView}}{{view App.SummaryView}}{{outlet mainArea}}{{outlet detailsArea}}application.handlebars
  67. 67. {{view App.NavigationView}}{{view App.SummaryView}}{{outlet mainArea}}{{outlet detailsArea}}application.handlebars
  68. 68. PurchasesIndex Viewing Editing
  69. 69. 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({})})
  70. 70. 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({})})})});
  71. 71. {{view App.NavigationView}}{{view App.SummaryView}}{{outlet mainArea}}{{outlet detailsArea}}application.handlebars
  72. 72. 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({})})})});
  73. 73. purchases: Ember.Route.extend({index: Ember.Route.extend({})})
  74. 74. 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);}})})
  75. 75. 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?
  76. 76. controller.connectOutlet(mainArea, purchaseList, purchases);
  77. 77. controller.connectOutlet(mainArea, purchaseList, purchases);App.PurchaseListView = Ember.View.extend({templateName: purchaseList});App.PurchaseListController = Ember.ArrayController.extend();
  78. 78. Ember.ArrayController.extend();proxy/presenter/controller/thingie
  79. 79. proxycontent
  80. 80. proxycontentwhat’s your length?
  81. 81. proxycontenthow are you sorted?
  82. 82. controller.connectOutlet(mainArea, purchaseList, purchases);App.PurchaseListView = Ember.View.extend({templateName: purchaseList});App.PurchaseListController = Ember.ArrayController.extend();
  83. 83. 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}}
  84. 84. 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}}
  85. 85. 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);}})})
  86. 86. 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?
  87. 87. controller.connectOutlet(detailsArea, map, locations);
  88. 88. App.MapView = Ember.View.extend({templateName: googleMap});App.MapController = Ember.ArrayController.extend();controller.connectOutlet(detailsArea, map, locations);
  89. 89. controller.connectOutlet(detailsArea, map, locations);App.MapView = Ember.View.extend({templateName: googleMap});App.MapController = Ember.ArrayController.extend();
  90. 90. controller.connectOutlet(detailsArea, map, locations);App.MapView = Ember.View.extend({templateName: googleMap});App.MapController = Ember.ArrayController.extend();
  91. 91. controller.connectOutlet(detailsArea, map, locations);App.MapView = Ember.View.extend({templateName: googleMap});App.MapController = Ember.ArrayController.extend();
  92. 92. PurchasesIndex Viewing Editing
  93. 93. PurchasesIndex Viewing Editing
  94. 94. {{#each purchase in controller}}<li>{{purchase.date}}{{purchase.location}}{{purchase.amount}}</li>{{/each}}
  95. 95. {{#each purchase in controller}}<li {{action showDetails purchase}}>{{purchase.date}}{{purchase.location}}{{purchase.amount}}</li>{{/each}}
  96. 96. purchases: Ember.Route.extend({index: Ember.Route.extend({connectOutlets: function(router){...}})})
  97. 97. purchases: Ember.Route.extend({index: Ember.Route.extend({showDetails: Ember.Route.transitionTo(‘viewing’),connectOutlets: function(router){...}})})
  98. 98. purchases: Ember.Route.extend({index: Ember.Route.extend({showDetails: Ember.Route.transitionTo(‘viewing’),connectOutlets: function(router){...}}),viewing: Ember.Route.extend({})})
  99. 99. PurchasesIndex Viewing Editing
  100. 100. PurchasesIndex Viewing Editing
  101. 101. 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?
  102. 102. 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);}})})
  103. 103. 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}}
  104. 104. controller.connectOutlet(detailsArea, purchaseDetails, context);
  105. 105. controller.connectOutlet(detailsArea, purchaseDetails, context);App.PurchaseDetailsView = Ember.View.extend({templateName: details});App.PurchaseDetailsController = Ember.ObjectController.extend();
  106. 106. controller.connectOutlet(detailsArea, purchaseDetails, context);App.PurchaseDetailsView = Ember.View.extend({templateName: details});App.PurchaseDetailsController = Ember.ObjectController.extend();
  107. 107. <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();
  108. 108. 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}}
  109. 109. Ember.ObjectController.extend();
  110. 110. proxycontentEmber.ObjectController.extend();
  111. 111. proxycontentare you pending?
  112. 112. 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}}
  113. 113. PurchasesIndex Viewing Editing
  114. 114. PurchasesIndex Viewing Editing
  115. 115. <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}}
  116. 116. 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);}})})
  117. 117. 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);}})})
  118. 118. 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);}})})
  119. 119. 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?
  120. 120. connectOutlet(detailsArea, editPurchaseDetails, context);
  121. 121. connectOutlet(detailsArea, editPurchaseDetails, context);App.EditPurchaseDetailsView = Ember.View.extend({templateName: edit-details});App.EditPurchaseDetailsController = Ember.ObjectController.extend();
  122. 122. 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.TextFieldvalueBinding="name"}}
  123. 123. 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.TextFieldvalueBinding="name"}}
  124. 124. Ember.ObjectController.extend();
  125. 125. proxycontentEmber.ObjectController.extend();
  126. 126. proxycontentwhat’s your name?
  127. 127. PurchasesIndex Viewing Editing
  128. 128. PurchasesIndex Viewing Editing
  129. 129. 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){...})})
  130. 130. PurchasesIndex Viewing Editing
  131. 131. Demeter’d
  132. 132. https://gist.github.com/3981133
  133. 133. > 7 views
  134. 134. which pattern, when?
  135. 135. • 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
  136. 136. • 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
  137. 137. • 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
  138. 138. MobileCocoa TouchAndroid SDKDesktopCocoa.NETWeb ?
  139. 139. MobileCocoa TouchAndroid SDKDesktopCocoa.NETWeb
  140. 140. User Interface HTML+CSSData PersistenceApplicationArchitecture
  141. 141. fin

×