EmberConf 2014: Mr. Router embraces the Controller - Alex Matchneer

E6138af35a4330451deb308e9db17752?s=47 machty
March 27, 2014

EmberConf 2014: Mr. Router embraces the Controller - Alex Matchneer

Query params, model dependent state, and the subtleties of the route vs controller paradigm.

E6138af35a4330451deb308e9db17752?s=128

machty

March 27, 2014
Tweet

Transcript

  1. The one where Mr. Router embraces the Controller Alex Matchneer

    EmberConf 2014
  2. “Mr. Router” Alex Matchneer Ember.js Core @machty

  3. Embracing the Controller: Overview • The State of Query Params

    • What’s the hold up? • The Missing Primitive • Demo • Roadmap • Questions?
  4. /?query=params Query Params

  5. A Brief History of Query Params • 2013: Alex Speller

    implements standalone QP lib, later merged into Canary • 2014: Alex Matchneer rewrites query params (query-params-new) • 2014: Alex Matchneer rewrites query params again (query-params-new-new-final- 2)
  6. - Everyone “When will query params land in beta?”

  7. - Mr. Router “Not much longer. It’s complicated.”

  8. - Everyone “I’m switching to Angular.”

  9. What’s taking so long? Get it together, core team

  10. Where should QPs live? The Router? • The original QP

    implementation was very route-centric: • Specify all QPs in the Router.map DSL • Optionally pass QP values to server queries • Optionally use QP values to set controller properties
  11. Problems with Route-centric QPs • Lots of hoops to jump

    through to get QPs into controllers. • Keeping QPs in sync w controller props was a manual, cumbersome, error-prone process
  12. ...we shifted the focus from routes to controllers... What if,

    instead...
  13. - Mr. Router, 3 months ago “What a dumb idea”

  14. - Mr. Router, 2.9 months ago “OK I’ll give it

    a shot”
  15. - Mr. Router, 14 days ago “Ah OK I finally

    get it”
  16. Query Params Today (query-params-new) • Controller-centric API App.ArticlesController = Ember.ArrayController.extend({

    queryParams: [‘sortBy’], sortBy: ‘publishDate’ // default value });
  17. Query Params Today (query-params-new) • Extremely simple API • QPs

    are bound to controller properties: update controller QP prop and URL will update, and vice versa • No need for custom observing / coalescing of controller QP property changes
  18. QPs: Default values simplify (de)serialization App.ArticlesController = Ember.ArrayController.extend({ queryParams: [‘page’],

    page: 1 // default value }); • When `page` === 1, URL is “/articles” • When `page` === 2, URL is “/articles?page=2” • If user clicks back button, intelligently deserialize into same type as default value; no need for parseInt, boolValue === “true”, etc
  19. QPs: and more... • Can opt into full on transition

    (re-fire model hooks) via config hash on Ember.Route • Can also control whether URL query param syncs use pushState/replaceState, etc
  20. Query Params • Available at your local Ember Canary

  21. but... that’s all well and good

  22. Isn’t it an API smell that you’ve taken this thing

    that’s predominantly been the responsibility of the router to manage and moved it to controllers? - Mr. Router and other h8rz, 17 days ago
  23. No. It is not. Let’s find out why...

  24. What is the job of a controller anyway? • Manage

    application state • Wrap the model with additional information to present to the template (also application state)
  25. What is the job of the router? • Navigation •

    Provide a link between URLs and hierarchies of controllers / templates
  26. Before query params, Ember app URLs consisted only of the

    path: /this/is/what/the/router/manages
  27. But that doesn’t mean that the router should should also

    necessarily be in charge of /?all=of&this=stuff
  28. So who should be in charge of /?all=of&this=stuff

  29. Why not controllers?

  30. But, but, URLs… they’re tied to the router! • Even

    URL-less Ember apps benefit greatly from the Ember Router App.Router.reopen({ location: 'none' });
  31. URL-less Ember Apps have • links between app state hierarchies

    (with link-to and transitionTo) • Deeply nested templates • Loading / error substates • Action bubbling • …and a beautiful API to manage and reason about the above very challenging use cases
  32. So the router still has its work cut out for

    it.
  33. Division of URL Labor • The router serializes hierarchy into

    the path • Controllers serialize app data into the query
  34. But keep in mind that query params are just one

    of many ways to serialize app state
  35. Serializing App State • It should be easy to swap

    methods of serialization • e.g. moving ?isActive=true to localStorage instead shouldn’t involve gutting your Router.map code
  36. Hopefully we can all agree that • the following is

    the wrong API: this.transitionTo(‘current.route’, { queryParams: { page: 3 } }); • really you should just be able to write: controller.set(‘page’, 3);
  37. Hopefully we can all agree (cont.) • transitionTo is a

    beautiful thing… • ...when you’re actually performing a hierarchical transition.
  38. - Everyone, one week from now “Good for you, you’ve

    figured it out! So why haven’t you shipped?”
  39. Devil is in the details: property stickiness • Enter a

    route • Set controller query param properties • Leave route • Come back into the route… • Are those QP properties remembered from before?
  40. Controller Property Stickiness • No One Right Answer (only some

    wrong ones) • Welcome to the Dismal Science of API Design • Goal: find the missing primitive, expose it as something that can be configured when necessary, but offer a high-level convention so that configuration is rare
  41. Can it be done? Can we find the missing primitive?

  42. Controller Property Stickiness Today • Route-driven controllers, once instantiated, live

    forever • Others have shorter lifecycles, e.g. item controllers
  43. Controller Property Stickiness Today • The sticky behavior we have

    today is coupled to controller lifecycle • Very convenient for the ArticlesController at /articles • Pretty undesirable / error-prone for the ArticlesShowController at /articles/some- title
  44. The Primitive? • The difference between the controllers at /articles

    vs /articles/some-title is that one has a uniquely identifiable model • Property stickiness for ArticlesController: desirable • Property stickiness for ArticlesShowController: only desirable when its model hasn’t changed • e.g. isSelected shouldn’t be preserved when switching b/w some-title and other-title articles
  45. The Primitive: Model Dependent State

  46. Model-dependent State • State, accessible to controllers, tied to a

    specific model • Store / restore controller properties scoped to the controller’s model • e.g. remember that article with ID ‘some-title’ isSelected, but ‘other-title’ is not
  47. Who consumes this primitive? • It’s a pattern for caching;

    any form of serialization is a consumer, e.g.: • Query Params • Local Storage • IndexedDB
  48. How is it implemented? • Inject a cache object onto

    all controllers • Each sticky property is responsible for computing a bucket key • Replace sticky properties with bindings into the cache object, keyed by bucket key
  49. How is it implemented? (cont.) • Bucket keys are computed

    properties; when they change, a new bucket is allocated (or a previous one is looked up) • Bucket keys based on the controller’s model ID give us… Model Dependent State
  50. Sounds hard? • Maybe at the low-level, but much potential

    for higher-level API • Thinking about bucket keys should ideally be rare
  51. Bucket as thou wilt • Full control over how a

    bucket is allocated • Could just be a POJO (memory cache, forgotten on page reload) • Could be your own LocalStorageProxy that writes into local storage as values change (remembered upon page reload)
  52. Query Params as a consumer • The question of stickiness

    is not a query params concern; QPs are a layer above
  53. Demo

  54. TL;DR • Decouple preserved app state from controller lifecycle

  55. Roadmap • Iterate on query-params-new-new-final-7 to use model-dep state •

    Later, provide higher-level API for property stickiness • In the meantime, collect feedback from the folk mucking around in the bucket key trenches
  56. Roadmap Dr. Router’s Vaporwarium Ember Canary

  57. Roadmap • Ship query params • Explore higher level APIs

    / conventions for model-dependent state
  58. Q/A

  59. Thank you! @machty Special Thanks: Alex Speller Ray Tiley