$30 off During Our Annual Pro Sale. View Details »

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.

Lauren Tan

July 13, 2016
Tweet

More Decks by Lauren Tan

Other Decks in Programming

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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"}}

    View Slide

  16. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    {{#shopping-cart items=items as |cart|}}

    {{#each cart.items as |item|}}
    {{item.name}} - {{item.quantity}} x {{item.price}}
    {{/each}}


    {{cart.total}}

    {{cart.checkout}}
    {{cart.empty}}

    {{/shopping-cart}}

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    return
    {this.props.posts.map((post) => {
    return
    })}
    ;

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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}}

    View Slide

  31. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    export default Component.extend({
    filteredItems: computed('[email protected]', function() {
    // ... complicated logic
    })
    });

    View Slide

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

    {{#each (filter-by 'price' priceRange items) as |items|}}
    {{item}}
    {{/each}}

    https://github.com/DockYard/ember-composable-helpers#filter-by

    View Slide

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

    View Slide

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

    View Slide

  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.”

    View Slide

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

    View Slide

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

    View Slide

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

    {{#each items as |item|}}
    {{item}}
    {{/each}}

    View Slide

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

    {this.props.items.map((item) => {
    {item}
    })}
    ;

    View Slide

  40. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    Greetings!
    Hello {{name}}, and welcome.

    View Slide

  41. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    Greetings!
    "Hello "
    "Jim Bob"
    ", and welcome."

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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}}

    View Slide

  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

    View Slide

  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.”

    View Slide

  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.”

    View Slide

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


    Hello World


    Hello, Example. Today's date and time is {{now}}.


    View Slide

  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);

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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);
    })
    });

    View Slide

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

    View Slide

  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
    })
    });

    View Slide

  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}}

    View Slide

  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}}

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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.”

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  73. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    1 + 1 = {{add 1 1}}

    View Slide

  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);

    View Slide

  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);

    View Slide

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

    View Slide

  77. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    Numbers: {{numbers}}
    Sorted: {{sort "ascending" numbers}}
    Numbers: {{numbers}}
    https://ember-twiddle.com/717d4c5f774d0b2822e853d67ef7c3a9?openFiles=templates.application.hbs%2C

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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();
    }
    }

    View Slide

  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);

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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"}}
    {{sum 1 2 3}}
    {{gt 1 2}}

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  91. EMBERCAMP 2016
    I CAN WRITE MY APP WITH NO HANDLEBARS
    {{#with (changeset user userValidations) as |changeset|}}
    {{input value=changeset.firstName}}
    Save
    {{/with}}

    View Slide

  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}}

    View Slide

  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"))
    }}

    View Slide

  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}}

    View Slide

  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}`);
    }
    }

    View Slide

  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(...);
    });

    View Slide

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

    View Slide

  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

    View Slide

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

    {{if isExpanded "I am expanded" "I am not"}}

    View Slide

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

    1-Click Buy

    https://github.com/DockYard/ember-composable-helpers#pipe

    View Slide

  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);

    View Slide

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

    View Slide

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

    View Slide

  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();
    })
    });

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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;
    }
    });

    View Slide

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

    View Slide

  110. I CAN WRITE MY APP WITH NO HANDLEBARS
    EMBERCAMP 2016
    {{#each (repeat 3) as |nil index|}}

    {{!some HTML block}}

    {{/each}}
    https://github.com/DockYard/ember-composable-helpers#repeat

    View Slide

  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}}

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  117. I CAN WRITE MY APP WITH NO HANDLEBARS
    EMBERCAMP 2016
    LAUREN TAN
    SUGARPIRATE_
    POTETO
    Thanks {{name}}!

    View Slide