Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Virtual EmberConf 2021: Platform State of the Union

Virtual EmberConf 2021: Platform State of the Union

Godfrey Chan

March 30, 2021
Tweet

More Decks by Godfrey Chan

Other Decks in Programming

Transcript

  1. Isaac Lee Jared Galanis Kenigbolo Meya Stephen Matthew Beale Melanie

    Sumner Tom Dale Jen Weber Krati Ahuja Alex Navasardyan Sivakumar Kailasam Framework Learning Steering Framework Steering Framework Framework Learning Learning CLI CLI Scott Newcomer Core Team Changes Learning Learning Data Steering Steering
  2. Improvements Modernized Built-In Components Production Build Templates Auto-Tracking Bug Fixes

    Better Error Messages Prettier New Lint Rules and Fixers <LinkTo> Now Works In Integration Tests Route Transition Debug Utilities ember-cli-update Inspector Collapsible Sidebar Better Component Inspector Test Harness QOL Improvements Template Lint TODOs Template Recast Whitespace Preservation New Polyfills and Codemods Website and Blog Redesign ember new !"lang {{page-title}} @ember!# ES Modules Unbundled ember-source RouterService#refresh()
  3. Improvements Modernized Built-In Components Production Build Templates Auto-Tracking Bug Fixes

    Better Error Messages Prettier New Lint Rules and Fixers <LinkTo> Now Works In Integration Tests Route Transition Debug Utilities ember-cli-update Inspector Collapsible Sidebar Better Component Inspector Test Harness QOL Improvements Template Lint TODOs Template Recast Whitespace Preservation New Polyfills and Codemods Website and Blog Redesign ember new !"lang {{page-title}} @ember!# ES Modules Unbundled ember-source RouterService#refresh()
  4. Improvements Modernized Built-In Components Production Build Templates Auto-Tracking Bug Fixes

    Better Error Messages Prettier New Lint Rules and Fixers <LinkTo> Now Works In Integration Tests Route Transition Debug Utilities ember-cli-update Inspector Collapsible Sidebar Better Component Inspector Test Harness QOL Improvements Template Lint TODOs Template Recast Whitespace Preservation New Polyfills and Codemods Website and Blog Redesign ember new !"lang {{page-title}} @ember!# ES Modules Unbundled ember-source RouterService#refresh()
  5. Improvements Modernized Built-In Components Production Build Templates Auto-Tracking Bug Fixes

    Better Error Messages Prettier New Lint Rules and Fixers <LinkTo> Now Works In Integration Tests Route Transition Debug Utilities ember-cli-update Inspector Collapsible Sidebar Better Component Inspector Test Harness QOL Improvements Template Lint TODOs Template Recast Whitespace Preservation New Polyfills and Codemods Website and Blog Redesign ember new !"lang {{page-title}} @ember!# ES Modules Unbundled ember-source RouterService#refresh()
  6. Improvements Modernized Built-In Components Production Build Templates Auto-Tracking Bug Fixes

    Better Error Messages Prettier New Lint Rules and Fixers <LinkTo> Now Works In Integration Tests Route Transition Debug Utilities ember-cli-update Inspector Collapsible Sidebar Better Component Inspector Test Harness QOL Improvements Template Lint TODOs Template Recast Whitespace Preservation New Polyfills and Codemods Website and Blog Redesign ember new !"lang {{page-title}} @ember!# ES Modules Unbundled ember-source RouterService#refresh()
  7. Improvements Modernized Built-In Components Production Build Templates Auto-Tracking Bug Fixes

    Better Error Messages Prettier New Lint Rules and Fixers <LinkTo> Now Works In Integration Tests Route Transition Debug Utilities ember-cli-update Inspector Collapsible Sidebar Better Component Inspector Test Harness QOL Improvements Template Lint TODOs Template Recast Whitespace Preservation New Polyfills and Codemods Website and Blog Redesign ember new !"lang {{page-title}} @ember!# ES Modules Unbundled ember-source RouterService#refresh()
  8. Deprecations getWithDefault tryInvoke String.prototype Extensions Ember.String Utilities {{#with}} <LinkTo> Positional

    Arguments Implicit Injections Old Browser Support Policy Implicit this Property Lookup {{attrs.*}} Packager Experiment Legacy classNameBindings Template Syntax Classic Edition Optional Features Old Manager Capabilities hasBlock and hasBlockParams Magic Variables Array Observers Ember.Component Features On Built-In Components Transition Methods On Routes and Controllers {{loc}} Ember Global
  9. Pit of Incoherence Pit of Incoherence Where We Are Where

    We Want to Be Incremental Change Octane Polaris
  10. Pit of Incoherence Pit of Incoherence Where We Are Where

    We Want to Be Incremental Change Octane Polaris
  11. Ember addons addons Addons You app You app You App

    app.js vendor.js index.html You app You app De-Facto Standard
  12. z

  13. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <!-- TODO: implement drop down component --> app/templates/index.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  14. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <!-- TODO: implement drop down component --> app/templates/index.hbs <div class="drop-down" ...attributes> <button type="button" id="{{this.id}}-button" aria-haspopup="listbox" aria-expanded={{if this.expanded "true" "false"}} {{on "click" this.toggle}}> {{@label}} </button> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island <DropDown @label="Choose a destination..." @items={{this.places}} />
  15. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} /> app/templates/index.hbs <div class="drop-down" ...attributes> <button type="button" id="{{this.id}}-button" aria-haspopup="listbox" aria-expanded={{if this.expanded "true" "false"}} {{on "click" this.toggle}}> {{@label}} </button> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  16. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} /> app/templates/index.hbs <div class="drop-down" ...attributes> <button type="button" id="{{this.id}}-button" aria-haspopup="listbox" aria-expanded={{if this.expanded "true" "false"}} {{on "click" this.toggle}}> {{@label}} </button> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  17. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} /> app/templates/index.hbs <div class="drop-down" ...attributes> <button type="button" id="{{this.id}}-button" aria-haspopup="listbox" aria-expanded={{if this.expanded "true" "false"}} {{on "click" this.toggle}}> {{@label}} </button> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  18. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <button type="button" id="{{this.id}}-button" aria-haspopup="listbox" aria-expanded={{if this.expanded "true" "false"}} {{on "click" this.toggle}}> {{@label}} </button> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> app/components/drop-down.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island </li> {{/each}} </ul> {{/if}} </div> {{yield item}}
  19. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <button type="button" id="{{this.id}}-button" aria-haspopup="listbox" aria-expanded={{if this.expanded "true" "false"}} {{on "click" this.toggle}}> {{@label}} </button> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item}} </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Destination Where would be the perfect destination for your
 virtual vacation? Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  20. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item}} </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Sorry, we are sold out. Join our mailing list to be notified of new tours! Destination Where would be the perfect destination for your
 virtual vacation?
  21. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item}} </li> {{else}} <li> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Choose a destination... Sorry, we are sold out. Join our mailing list to be notified of new tours! Destination Where would be the perfect destination for your
 virtual vacation?
  22. <h2>Destination</h2> <p>Where would be the perfect destination for your virtual

    vacation?</p> <DropDown @label="Choose a destination..." @items={{this.places}} as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item}} </li> {{else}} <li> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  23. <!-- Omitted: same markup for header as before --> <DropDown

    @label="Choose a destination..." @items={{this.places}} as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item}} </li> {{/each}} </ul> {{/if}} </div> app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  24. <!-- Omitted: same markup for header as before --> <DropDown

    @label="Choose a destination..." @items={{this.places}}> <:banner> <h3>Too much free time?</h3> Become a virtual tour guide and share your passion with the world! </:banner> <:item as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </:item> <:empty> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </:empty> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <div class="banner">{{yield to="banner"}}</div> <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item to="item"}} </li> {{else}} <li>{{yield to="empty"}}</li> {{/each}} </ul> {{/if}} app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  25. <!-- Omitted: same markup for header as before --> <DropDown

    @label="Choose a destination..." @items={{this.places}}> <:banner> <h3>Too much free time?</h3> Become a virtual tour guide and share your passion with the world! </:banner> <:item as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </:item> <:empty> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </:empty> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <div class="banner">{{yield to="banner"}}</div> <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item to="item"}} </li> {{else}} <li>{{yield to="empty"}}</li> {{/each}} </ul> {{/if}} app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  26. <!-- Omitted: same markup for header as before --> <DropDown

    @label="Choose a destination..." @items={{this.places}}> <:banner> <h3>Too much free time?</h3> Become a virtual tour guide and share your passion with the world! </:banner> <:item as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </:item> <:empty> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </:empty> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <div class="banner">{{yield to="banner"}}</div> <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item to="item"}} </li> {{else}} <li>{{yield to="empty"}}</li> {{/each}} </ul> {{/if}} app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  27. <!-- Omitted: same markup for header as before --> <DropDown

    @label="Choose a destination..." @items={{this.places}}> <:banner> <h3>Too much free time?</h3> Become a virtual tour guide and share your passion with the world! </:banner> <:item as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </:item> <:empty> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </:empty> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <div class="banner">{{yield to="banner"}}</div> <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item to="item"}} </li> {{else}} <li>{{yield to="empty"}}</li> {{/each}} </ul> {{/if}} app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Sorry, we are sold out. Join our mailing list to be notified of new tours!
  28. <!-- Omitted: same markup for header as before --> <DropDown

    @label="Choose a destination..." @items={{this.places}}> <:banner> <h3>Too much free time?</h3> Become a virtual tour guide and share your passion with the world! </:banner> <:item as |item|> <img src={{item.image}} alt="" width="80" height="80"> <h3>{{item.name}}</h3> <p>{{item.description}}</p> </:item> <:empty> <h3>Sorry, we are sold out.</h3> <a href="...">Join our mailing list</a> to be notified of new tours! </:empty> </DropDown> app/templates/index.hbs <div class="drop-down" ...attributes> <!-- Omitted: same markup for button as before --> {{#if this.expanded}} <div class="banner">{{yield to="banner"}}</div> <ul role="listbox" aria-labelledby="{{this.id}}-button" tabindex="-1"> {{#each @items as |item|}} <li role="option" {{on "click" (fn this.select item)}}> {{yield item to="item"}} </li> {{else}} <li>{{yield to="empty"}}</li> {{/each}} </ul> {{/if}} app/components/drop-down.hbs Destination Where would be the perfect destination for your
 virtual vacation? Choose a destination... Too much free time? Become a virtual tour guide and share your passion with the world! Yufuin Scenic Japanese Onsen Town Paris Capital of France O‘ahu Third Largest Hawaiian Island
  29. {{yield "one" "two" "three" to="thing"}} <:thing as |first second third|>

    First: {{first}} Second: {{second}} Third: {{third}} </:thing>
  30. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  31. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  32. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  33. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  34. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  35. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  36. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea @label="Bio" @field="bio" /> <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit
  37. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit @label="Bio" @field="bio" /> @form={{...}}
  38. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> <f.TextArea <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> <f.Submit /> </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield (hash Input=(component "my-form/input" form=this) TextArea=(component "my-form/text-area" form=this) Combo=(component "my-form/combo" form=this) Submit=(component "my-form/submit" form=this) ) }} </form> app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit @label="Bio" @field="bio" />
  39. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as | < < < < </MyForm> app/templates/index.hbs <form {{on "submit" this.onSubmit}}> {{yield app/components/my-form.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit Input TextArea Combo Submit|> Input @label="Your Name" @field="name" /> TextArea @label="Bio" @field="bio" /> Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> Submit /> (component "my-form/input" form=this) (component "my-form/text-area" form=this) (component "my-form/combo" form=this) (component "my-form/submit" form=this) }} </form>
  40. NAMED BLOCKS are called by the component author and can

    receive data from the component via block params. CONTEXTUAL COMPONENTS are called by the component consumer and can pass data to the component via arguments and blocks.
  41. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> {{#if (f.isEmpty "name")}} <f.Error @field="name">Please provide your name.</f.Error> {{/if}} <f.TextArea @label="Bio" @field="bio" {{f.autoResize}} /> {{#if (f.hasMinLength "bio" 100)}} <f.Error @field="bio">Your bio is too short.</f.Error> {{else if (f.hasMaxLength "bio" 500)}} <f.Error @field="bio">Your bio is too long.</f.Error> {{/if}} <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> {{#if (f.hasMinLength "visited" 3)}} Wow, you visited a lot of countries! You will be a great tour guide! {{/if}} <f.Submit /> </MyForm> app/templates/index.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit Your bio is too short. Wow, you visited a lot of countries! You will be a great tour guide!
  42. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> {{#if (f.isEmpty "name")}} <f.Error @field="name">Please provide your name.</f.Error> {{/if}} <f.TextArea @label="Bio" @field="bio" {{f.autoResize}} /> {{#if (f.hasMinLength "bio" 100)}} <f.Error @field="bio">Your bio is too short.</f.Error> {{else if (f.hasMaxLength "bio" 500)}} <f.Error @field="bio">Your bio is too long.</f.Error> {{/if}} <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> {{#if (f.hasMinLength "visited" 3)}} Wow, you visited a lot of countries! You will be a great tour guide! {{/if}} <f.Submit /> </MyForm> app/templates/index.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit Your bio is too short. Wow, you visited a lot of countries! You will be a great tour guide!
  43. <h2>Application Form</h2> <p>Become a virtual tour guide and share your

    passion with the world!</p> <MyForm @onSubmit={{this.onSubmit}} as |f|> <f.Input @label="Your Name" @field="name" /> {{#if (f.isEmpty "name")}} <f.Error @field="name">Please provide your name.</f.Error> {{/if}} <f.TextArea @label="Bio" @field="bio" {{f.autoResize}} /> {{#if (f.hasMinLength "bio" 100)}} <f.Error @field="bio">Your bio is too short.</f.Error> {{else if (f.hasMaxLength "bio" 500)}} <f.Error @field="bio">Your bio is too long.</f.Error> {{/if}} <f.Combo @label="Countries Visited" @field="visited" @options={{this.countries}} /> {{#if (f.hasMinLength "visited" 3)}} Wow, you visited a lot of countries! You will be a great tour guide! {{/if}} <f.Submit /> </MyForm> app/templates/index.hbs Application Form Become a virtual tour guide and share your passion with the world! Tomster Your Name Bio Countries Visited France Japan United States Submit Your bio is too short. Wow, you visited a lot of countries! You will be a great tour guide!
  44. import Component from '@glimmer/component'; import { SomeComponent, someModifier, someHelper }

    from 'some-addon'; export default class MyComponent extends Component { SomeComponent = SomeComponent; someModifier = someModifier; someHelper = someHelper; } app/components/my-component.js <this.SomeComponent @foo="bar" /> <div {{this.someModifier foo="bar"}}>...</div> {{#if (this.someHelper foo="bar")}}...{{/if}} app/components/my-component.hbs new!
  45. import Component from '@glimmer/component'; import { SomeComponent, someModifier, someHelper }

    from 'some-addon'; export default class MyComponent extends Component { SomeComponent = SomeComponent; someModifier = someModifier; someHelper = someHelper; } app/components/my-component.js {{yield (hash SomeComponent=(component this.SomeComponent foo="bar") someModifier=(modifier this.someModifier foo="bar") someHelper=(helper this.someHelper foo="bar") ) }} app/components/my-component.hbs new!
  46. import Component from '@glimmer/component'; import { helper } from '@ember/component/helper';

    function sum(numbers) { if (numbers.length === 0) { return 0; } else { return numbers.pop() + sum(numbers); } } export default class MyComponent extends Component { sumHelper = helper(sum); } app/components/my-component.js Sum of 1 to 5: {{this.sumHelper 1 2 3 4 5}} app/components/my-component.hbs new!
  47. import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking';

    import Modifier from 'ember-modifier'; export default class MyComponent extends Component { @tracked width = 100; @tracked height = 100; get keepInViewport() { let { width, height } = this; return class extends Modifier { didInstall() { // ... } willRemove() { // ... } }; } } app/components/my-component.js <img src={{@src}} width={{this.width}} height={{this.height}} ...attributes {{this.keepInViewport}}> app/components/my-component.hbs new!
  48. import Component from '@glimmer/component'; export default class MyComponent extends Component

    { SomeComponent = SomeComponent; someModifier = someModifier; someHelper = someHelper; } app/components/my-component.js app/components/my-component.hbs new! import { SomeComponent, someModifier, someHelper } from 'some-addon'; this. this. this. SomeComponent @foo="bar" /> someModifier foo="bar"}}>...</div> someHelper foo="bar")}}...{{/if}} < <div {{ {{#if (
  49. app/components/my-component.hbs not yet! import { SomeComponent, someModifier, someHelper } from

    'some-addon'; --- --- SomeComponent @foo="bar" /> someModifier foo="bar"}}>...</div> someHelper foo="bar")}}...{{/if}} < <div {{ {{#if (
  50. app/components/my-component.hbs not yet! import { SomeComponent, someModifier, someHelper } from

    'some-addon'; <SomeComponent @foo="bar" /> <div {{someModifier foo="bar"}}>...</div> {{#if (someHelper foo="bar")}}...{{/if}} app/components/my-component.js import { SomeComponent, someModifier, someHelper } from 'some-addon'; --- --- <SomeComponent @foo="bar" /> <div {{someModifier foo="bar"}}>...</div> {{#if (someHelper foo="bar")}}...{{/if}} import Component from '@glimmer/component'; import { helper } from '@ember/component/helper'; const sum = helper(function sum(numbers) { if (numbers.length === 0) { return 0; } else { return numbers.pop() + sum(numbers); } }); export default class MyComponent extends Component { <template> Sum of 1 to 5: {{sum 1 2 3 4 5}} </template> // other JS things... }
  51. #287 {{#in-element}} New Primitives #615 Auto-Tracking Memoization #669 Tracked Storage

    #625 Helper Manager #626 invokeHelper #496 Strict Mode Templates #581 New Test Waiters #580 Destroyables
  52. #617 RFC Stages Process Updates Accessibility Working Group TypeScript Strike

    Team Tooling #649 Deprecation Staging Polyfills Codemods #685 New Browser Support Policy