from other frameworks ‣ Virtual DOM-based rendering (Glimmer) ‣ Virtual Machine-powered rendering as of 2.10 (Glimmer 2) ‣ Component focused ‣ Data-down, actions up ‣ Shed lots of SproutCore legacy and simplified APIs
‣ Test runner ‣ Build tool ‣ Add-on system ‣ Deployment (via ember-cli-deploy add-on) ‣ Glimmer 2 ‣ Virtual Machine-based ‣ Fastest DOM updates in the game ‣ Highly competitive initial render times ‣ Extracted to a component library (glimmerjs.com) ‣ One of the coolest things in JavaScript right now
general, data should be loaded by a route. ▸ Routes can be nested ▸ Data is cached and can be shared by child routes, making transitions quick. ▸ Sets up a controller with a context ▸ Provides template where child components or child routes will render. import Ember from 'ember'; const { Route } = Ember; export default Route.extend({ async model() { const data = await this.store.query('blog-post', { sortBy: 'name' }); return data; }, setupController(controller, model) { this._super(controller, model); const featuredPost = model.find(post => post.featured); controller.set('featuredPost', featuredPost); controller.set('posts', model); } });
between routes and the template context (i.e. your UI) ▸ Have access to the router ▸ Often used for working with URL query parameters, reloading the route, or transitioning to other routes ▸ Typically created implicitly by the route, but can be standalone ▸ Responsible for rendering the template for the route ▸ Won’t be necessary someday (maybe) ▸ Google Popular Internet Search Engine search: 'ember routable components'
in frameworks like React, Polymer, Angular 2 etc. ▸Isolated context, data must be passed in via attributes ▸Lifecycle hooks for control and extension of rendering ▸Can respond to actions from child components import Ember from 'ember'; const { Component, computed } = Ember; const YES_EXTRAORDINARILY_CONTRIVED = true; const NO_TOTALLY_NORMAL = false; export default Component.extend({ isSum41: computed('sum', function() { return Number(this.get('sum')) === 41; }), isThisContrived: computed('isMeetupTalk', function() { if (this.get('isMeetupTalk')) { return NO_TOTALLY_NORMAL; } else { return YES_EXTRAORDINARILY_CONTRIVED; } }), didRender() { console.log('yo I just did a render'); }, actions: { someAction(magicNumber) { this.set('msg', "OH GOD IT HURTS PLEASE NO CLICKY"); this.set('sum', this.get('sum') + magicNumber); } } });
in a template ▸ No access to application state ▸ Don’t try to pass a bunch of application state into them either ▸ Composable ▸ Hugely powerful ▸ e.g. {{component}} helper allows for composable components ▸ Essentially LISP in your templates <div class="best-in-class"> {{#if (and (or arePureFunctions isLispLike) (or aProperty someOtherProperty) (not usedResponsiblyInThisInstance) ) }} <strong>Helpers are rad! </strong> {{component aNameOfAComponent title='awesome'}} {{/if}} </div> app/templates/components/a-component.hbs
{ // Here we grab the full collection // of data from the parent route, which // has already been loaded. return this.modelFor('whats-good'); }, // This sets the context for the routes // template. Things set on the controller // are accessible in the route template. setupController(controller, model) { this._super( ...arguments); controller.set('goodThings', model); } }); app/routes/whats-good/index.js <h1>What's Good </h1> {{#good-things-list goodThings=goodThings as |goodThing|}} <div class="some-wrapping-class-with-style"> {{good-things-list-item goodThing=goodThing}} </div> {{/good-things-list}} <h5> Thank you Excellent People Who Make/Do Good Things </h5> app/templates/whats-good/index.hbs
to us. Here they are: </p> <ul class="good-things-list"> {{#each sortedGoodThings as |goodThing|}} <li class="good-things-list-item"> {{yield goodThing}} </li> {{/each}} </ul> import Ember from 'ember'; const { Component, computed } = Ember; export default Component.extend({ // Computed properties are push-based, lazily-evaluated, and // cached (usually) based on other properties. // When a dependent property changes, the cache is invalidated // and the expression is reevaluated next time it is accessed. numGoodThings: computed('goodThings.[]', function() { return this.get('goodThings').length; }), goodThingsAsc: ['attributes.name'], sortedGoodThings: computed.sort('goodThings', 'goodThingsAsc'), }); app/components/good-things-list.js app/templates/components/good-things-list.hbs <strong> {{#link-to 'whats-good.show' goodThing.attributes.slug}} {{titleize goodThing.attributes.name}} {{/link-to}} </strong> <p> <em>{{goodThing.attributes.description}} </em> </p> app/templates/components/good-things-list-item.hbs export function titleize(params) { const [text] = params; return text.split(" ").map((word) => { return EmberString.capitalize(word); }).join(' '); } // This makes the helper function available in // the templates. export default Helper.helper(titleize); app/helpers/titleize.js
‣ Built & supported by Ember Core Team ‣ Maps models to REST(ish) API resources ‣ Natively speaks JSON-API standard (jsonapi.org) ‣ Heavily customizable to work with nearly any resource-based API pattern you may have ‣ Becomes more complex the more non- standard your API patterns are ‣ Data is fetched, cached, and modified via an injectable service singleton called the Store ‣ Like backend ORMs, it has many gotchas import DS from 'ember-data'; const { Model, attr, hasMany, belongsTo } = DS; export default Model.extend({ slug: attr('string'), title: attr('string'), body: attr('string'), bodyHtml: attr('string'), published: attr('boolean'), publishedOn: attr('date'), createdAt: attr('date'), updatedAt: attr('date'), categories: hasMany('post-category', { async: false }), user: belongsTo('user', { async: false }), links: attr() }); @emberjs/data
middleware (redux-saga, etc.) ‣ Works with redux-devtools ‣ Fairly mature and well-supported ‣ I’ve never used this, but I heard it’s good ‣ I have no code for this slide ‣ Here’s a picture of a puppy @ember-redux/ember-redux
‣ Supports OAuth2 Resource Owner Password Credential Grant and Implicit Grant flows out- of-the-box ‣ Pluggable for other authentication schemes ‣ Devise (rails gem) ‣ GitHub, LinkedIn, Google, Facebook, etc. (via Torii authenticator) ‣ Provides a session service and route mixin to control where auth is required @simplabs/ember-simple-auth
on/with arrays, objects, and actions. ‣ e.g. map, reduce, filter, group-by ‣ Allows for composable chains of actions using the pipe helper ‣ e.g. (pipe sendMessage fetchLatest updateUnreadCount) ‣ Very useful for DRYing out components ‣ Makes your templates more LISP-y and declarative @DockYard/ember-composable-helpers
in the DOM ‣ Primary use cases: ‣ Modals ‣ Tooltips/popovers ‣ Top-level UI that depends on nested context @yapplabs/ember-wormhole {{#ember-wormhole to="floating-clippy"}} <div> {{#unhelpful-clippy}} <p> I see you're trying to give a talk on a JavaScript framework ... </p> <p> Can I get you to stop? </p> {{/unhelpful-clippy}} </div> {{/ember-wormhole}}
People O N Edge kNow what I’m Talkin’ bout Hey Everyone Let’s Practice Ember Real Soon Soo… Everyone Realizes Very much it Is time to Come to Ember, right? Mnemonic