EmberCamp 2016 – I Can Write My App With No Handlebars: Declarative Templating in Ember

EmberCamp 2016 – I Can Write My App With No Handlebars: Declarative Templating in Ember

Video: https://www.youtube.com/watch?v=ZpM4cV7rfj0&list=PL4eq2DPpyBbmrPasP06vK7cUkPUCNn_rW&index=3

In Ember, Handlebars is a small, Lisp-y language we use to express our application's user interface. We use Keywords, Helpers and Components and other primitives to build upon this language, and the result of this is a larger vocabulary in which we can declare our intent much more clearly. Let's explore how Keywords and Helpers augment Handlebars, and cover techniques and patterns for creating our own Helpers in good taste.

C8fccffc013096c4b465b50c284a5208?s=128

Lauren Tan

July 13, 2016
Tweet

Transcript

  1. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I CAN WRITE MY APP WITH NO HANDLEBARS Declarative Templating in Ember.js
  2. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    LAUREN TAN SUGARPIRATE_ POTETO
  3. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

  4. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    ZELDA
  5. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

  6. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

  7. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    LOOK MA, NO HANDS!
  8. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

  9. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

  10. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    https://www.youtube.com/watch?v=_ahvzDzKdB0
  11. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    LARGE small
  12. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    “A language design can no longer be a thing. It must be a pattern – a pattern for growth – a pattern for growing the pattern for defining the patterns that programmers can use for their real work and their main goal.” –Guy Steele
  13. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    wat
  14. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  15. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{get person propertyName}} {{t "user.edit.title"}} {{user-edit user=user}} {{if foo "bar" "baz"}}
  16. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#shopping-cart items=items as |cart|}} <ul> {{#each cart.items as |item|}} <li>{{item.name}} - {{item.quantity}} x {{item.price}}</li> {{/each}} </ul> <hr> {{cart.total}} <div class="button-group"> {{cart.checkout}} {{cart.empty}} </div> {{/shopping-cart}}
  17. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  18. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  19. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  20. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    LOGICLESS TEMPLATES?
  21. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    no logic extreme logic some logic
  22. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    let Plates = require('plates'); let html = '<div id="test">Old Value</div>'; let data = { test: 'New Value }; let output = Plates.bind(html, data);
  23. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    let age = 18; hbs`{{if (gt age 21) Adult Kid}}`;
  24. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    return <ul> {this.props.posts.map((post) => { return <Item key={post.objectId} post={post}/> })} </ul>;
  25. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    IT DEPENDS™
  26. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    SEPARATION OF CONCERNS
  27. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{if (gt age 21) Adult Kid}}
  28. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function gt([a, b]) { return a > b; } export default helper(gt);
  29. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    AVOID PROGRAMMING IN THE TEMPLATE
  30. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#unless (or (and (gte value 0) (lt value 0.0001)) (and (lt value 0) (not allowNegativeResults)))}} ... {{/unless}}
  31. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export default Component.extend({ filteredItems: computed('items.@each.price', function() { // ... complicated logic }) });
  32. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <ul> {{#each (filter-by 'price' priceRange items) as |items|}} <li>{{item}}</li> {{/each}} </ul> https://github.com/DockYard/ember-composable-helpers#filter-by
  33. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    RULE OF LEAST POWER
  34. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  35. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    “The "Rule of Least Power" suggests choosing the least powerful language suitable for a given purpose.”
  36. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    least powerful most powerful
  37. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    no logic extreme logic
  38. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <ul> {{#each items as |item|}} <li>{{item}}</li> {{/each}} </ul>
  39. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <ul> {this.props.items.map((item) => { <li>{item}</li> })} </ul>;
  40. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <h1>Greetings!</h1> <p>Hello {{name}}, and welcome.</p>
  41. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <h1>Greetings!</h1> <p>"Hello " "Jim Bob" ", and welcome."</p>
  42. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    el.childNodes; // ["Hello ", "Jim Bob", ", and welcome."]
  43. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  44. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    KEYWORD VS HELPER
  45. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    action component concat debugger each each-in get hash if input link-to loc log mut outlet partial query-params render textarea unbound unless with
  46. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function foo([model, value], { async }) { // .. } export default helper(foo); {{foo model "abc" async=true}}
  47. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  48. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    –Harold Abelson “Programs are meant to be read by humans and only incidentally for computers to execute.”
  49. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    –Unknown “Every line of code is a liability. The code that's easiest to maintain is code that was never written.”
  50. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <html> <head> <title>Hello World</title> </head> <body> Hello, Example. Today's date and time is {{now}}. </body> </html>
  51. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    page = Page(); head = new Head(); title = new Title(); title.setText("Hello World") head.add(title); page.add(head); body = new Body(); preamble = new StringBuffer(); preamble.append("Hello, Example. Today's date and time is "); preamble.append(Time.now().toString()); preamble.append("."); body.add(preamble.toString()); page.add(body);
  52. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    DECLARATIVE TEMPLATING
  53. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    no logic extreme logic some logic
  54. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#each filteredEmployees as |employee|}} ... {{/each}}
  55. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export default Component.extend({ employeeFilter: 'active', filteredEmployees: filter('employees', function() { let filter = get(this, 'employeeFilter'); return get(this, 'employees').filterBy('type', filter); }) });
  56. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#each-in groupedEmployees as |group employees|}} ... {{/each-in}}
  57. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export default Component.extend({ employeeGroupBy: 'role', filteredEmployees: /* ... */, groupedEmployees: computed('employeeGroupBy', 'filteredEmployees', function() { // ... complicated logic }) });
  58. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    https://github.com/DockYard/ember-composable-helpers {{#with (filter-by "type" employeeFilter employees) as |filteredEmployees|}} {{#each-in (group-by employeeGroupBy filteredEmployees) as |group employees|}} ... {{/each-in}} {{/with}}
  59. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    https://github.com/DockYard/ember-composable-helpers {{#each-in (group-by employeeGroupBy (filter-by "type" employeeFilter employees)) as |group employees|}} ... {{/each-in}}
  60. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  61. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    IT DEPENDS™
  62. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    no logic extreme logic some logic
  63. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  64. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#form-for person as |f|}} {{f.text-field "firstName"}} {{f.text-field "lastName"}} {{f.date-field "birthDate"}} {{f.submit "Save"}} {{/form-for}} https://github.com/martndemus/ember-form-for
  65. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  66. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  67. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    –Jiro Ono “In order to make delicious food, you must eat delicious food. […] Without good taste, you can’t make good food.”
  68. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    GOOD TASTE, GOOD CODE
  69. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    EMBERGER HELPERS
  70. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    HELPER PURITY
  71. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{capitalize "hello"}} {{capitalize "hello"}}
  72. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function add([a, b]) { return a + b; } export default helper(add);
  73. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <p>1 + 1 = {{add 1 1}}</p>
  74. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    let x = 0; export function add([a]) { return x += a; } export default helper(add);
  75. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    const sortMap = { ascending(a, b) { return a - b; }, descending(a, b) { return b - a; } }; export function sort([order, items]) { let sortFn = sortMap[order] || K; items.sort(sortFn); return items; } export default helper(append);
  76. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  77. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    Numbers: {{numbers}} <!-- 2, 5, 4, 1, 3 --> Sorted: {{sort "ascending" numbers}} <!-- 1, 2, 3, 4, 5 --> Numbers: {{numbers}} <!-- 1, 2, 3, 4, 5 --> https://ember-twiddle.com/717d4c5f774d0b2822e853d67ef7c3a9?openFiles=templates.application.hbs%2C
  78. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  79. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    EMBER-CHANGESET https://github.com/DockYard/ember-changeset
  80. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  81. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function changeset(obj, validateFn, validationMap) { return EmberObject.extend({ // changeset implementation }); } export default class Changeset { constructor() { return changeset(...arguments).create(); } }
  82. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function changeset([model, validationMap]) { if (isObject(validationMap)) { return new Changeset(model, lookupValidator(validationMap), validationMap); } return new Changeset(model, validationMap); } export default helper(changeset);
  83. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{dummy-form changeset=(changeset user EmployeeValidations) submit=(action "submit") rollback=(action "rollback") }} https://bit.ly/ember-changeset-demo
  84. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    RETURN VALUES
  85. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    STRINGS, NUMBERS, BOOLEANS
  86. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function join([separator, ...words]) { return words.join(separator); } export default helper(join); export function sum([...values]) { return values.reduce((acc, curr) => { return acc + curr; }, 0); } export default helper(join); export function gt([a, b]) { return a > b; } export default helper(gt); {{join "-" "hello" "world"}} <!-- hello-world --> {{sum 1 2 3}} <!-- 6 --> {{gt 1 2}} <!-- false -->
  87. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    ARRAYS
  88. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function repeat([length, value]) { if (typeOf(length) !== 'number') { return [value]; } return Array.apply(null, { length }).map(() => value); } export default helper(repeat); https://github.com/DockYard/ember-composable-helpers#repeat
  89. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#each (repeat 3 "Developers") as |text|}} {{text}} {{/each}} <!-- Developers Developers Developers -->
  90. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    OBJECTS
  91. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#with (changeset user userValidations) as |changeset|}} {{input value=changeset.firstName}} <button type="submit" {{action "save" changeset}}>Save</button> {{/with}}
  92. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{#with (hash bar=(component "hello-world")) as |foo|}} {{foo.bar name="Jim Bob"}} {{/with}} <!-- Hello world, Jim Bob! -->
  93. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{complex-component ui=(hash map=(component "user-map") sidebar=(component "user-sidebar") form=(component "user-form")) }}
  94. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{ui.sidebar user=user}} {{ui.map lat=user.lat lng=user.lng}} {{ui.form user=user}}
  95. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function registerDummyComponent(context, name = 'dummy-component', opts = {}) { let owner = getOwner(context); let options = assign({ tagName: 'dummy' }, opts); let DummyComponent = Component.extend(options); unregisterDummyComponent(context, name); owner.register(`component:${name}`, DummyComponent); } export function unregisterDummyComponent(context, name = 'dummy-component') { let owner = getOwner(context); if (owner.resolveRegistration(`component:${name}`)) { owner.unregister(`component:${name}`); } }
  96. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    test('it does something', function(assert) { registerDummyComponent(this); this.set('user', { firstName: 'Jim', lat: 51.5074, lng: 0.1278 }); this.on('submit', K); this.render(hbs` {{complex-component ui=(hash map=(component "dummy-component") sidebar=(component "dummy-component") form=(component "dummy-component")) }} `); assert.ok(...); });
  97. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    FUNCTIONS
  98. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function toggle([obj, prop]) { return function() { set(obj, prop, !get(obj, prop)); }; } https://github.com/DockYard/ember-composable-helpers#toggle
  99. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <button {{action (toggle "isExpanded" this)}}> {{if isExpanded "I am expanded" "I am not"}} </button>
  100. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    <button {{action (pipe addToCart purchase redirectToThankYouPage) item}}> 1-Click Buy </button> https://github.com/DockYard/ember-composable-helpers#pipe
  101. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export function pipe(actions = []) { return function(...args) { return actions.reduce((acc, curr, idx) => { if (idx === 0) { return curr(...args); } return invokeFunction(acc, curr); }, undefined); }; } export default helper(pipe);
  102. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    CLASS BASED HELPER
  103. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    {{t "user.edit"}}
  104. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    export default Helper.extend({ localesService: inject.service('locales'), currentLocale: readOnly('localesService.currentLocale'), compute([key]) { let currentLocale = get(this, 'currentLocale'); return get(this, 'localesService').lookup(currentLocale, key); }, localeDidChange: observer('currentLocale', function() { this.recompute(); }) });
  105. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

  106. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    COMPUTED VS HELPER
  107. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    COMPUTEDS
  108. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    export default Component.extend({ @computed('payment', 'rate', 'periods') annuity(payment, rate, periods) { let factor = ((1 - Math.pow(1 + rate, -periods)) / rate); return payment * factor; } });
  109. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    HELPERS
  110. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    {{#each (repeat 3) as |nil index|}} <div data-thing={{concat "foo-" index}}> {{!some HTML block}} </div> {{/each}} https://github.com/DockYard/ember-composable-helpers#repeat
  111. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    {{#unless (or (and (gte value 0) (lt value 0.0001)) (and (lt value 0) (not allowNegativeResults)))}} ... {{/unless}}
  112. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    TL;DR
  113. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  114. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  115. EMBERCAMP 2016 I CAN WRITE MY APP WITH NO HANDLEBARS

    I. Templates are Data II. Your UI as a Language III.Helper Best Practices
  116. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    LAUREN TAN SUGARPIRATE_ POTETO
  117. I CAN WRITE MY APP WITH NO HANDLEBARS EMBERCAMP 2016

    LAUREN TAN SUGARPIRATE_ POTETO Thanks {{name}}!