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

Ember.js & The Ecosystem

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

Ember.js & The Ecosystem

A slide-deck from a talk I did at JavaScriptMN on Ember.js and the ecosystem.

Avatar for Max Fierke

Max Fierke

August 31, 2017

Other Decks in Programming

Transcript

  1. I Live in Minneapolis. I Work-in-Place in Saint Paul. I

    Listen to Prince. I Shop at Target. It’s Lund’s not Byerly’s.
  2. I work for Iora Health. We’re based in Boston. We

    are Restoring Humanity to Health Care. I work the Full Stack™, But often build SPAs. I visit Dunkin’ Donuts. Every Quarter.
  3. 2011 - 2015
 Ember.js < 1.13 ‣ Two-way binding ‣

    Controller and view focused ‣ Shared context, little isolation. ‣ Vestigial SproutCore constructs ‣ View widgets ‣ ArrayController, CollectionView, and
 other Cocoa-inspired APIs.
  4. 2015 - present
 Ember.js 2.x ‣ Adoption of common patterns

    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
  5. Technical Innovations in Modern Ember ‣ Ember-CLI ‣ Code generation

    ‣ 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
  6. import Ember from 'ember'; const { computed } = Ember;

    const GOOD_PETS = ['doggo', 'pupper', 'dog', 'puppy', 'catdog']; const Pet = Ember.Object.extend({ isGood: computed('type', function() { return GOOD_PETS.includes(this.get('type')); }) }); const dog = Pet.create({ type: 'doggo' }); dog.get('isGood') // => true dog.set('type', 'dragon'); // updates type attribute // blows away caches on dependent properties dog.get('isGood') // => false Ember.Object Most (all?) Framework Objects extend Ember.Object
  7. EMBER FUNDAMENTALS Routes ▸ Responsible for the URL ▸ In

    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); } });
  8. TL;DR: you can usually get by without them* *because they’re

    typically generated automatically by the route
  9. EMBER FUNDAMENTALS Controllers: The Fine Print ▸ Effectively the glue

    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'
  10. EMBER FUNDAMENTALS Components ▸Represent pieces of UI ▸Analogous to 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); } } });
  11. EMBER FUNDAMENTALS Helpers ▸ Pure functions that can be used

    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
  12. app/router.js ... Router.map(function() { this.route('whats-good', function() { this.route('show', { path:

    '/:slug' }); }); }); ... GET /whats-good app/routes/whats-good.js import Ember from 'ember'; import fetch from 'fetch'; const { Route } = Ember; export default Route.extend({ // This hook fetches the data needed for the route. async model() { const goodThingsRequest = await fetch('/api/good-things', { headers: new Headers({ 'Accept': 'application/json' }) }); const { data: goodThings } = await goodThingsRequest.json(); return goodThings; } }); {{outlet}} app/templates/whats-good.hbs
  13. GET /whats-good import Ember from 'ember'; export default Ember.Route.extend({ model()

    { // 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
  14. GET /whats-good <p>There are {{numGoodThings}} good people & things known

    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
  15. ember-concurrency ‣ Task primitive for async work management ‣ Debouncing

    ‣ Restarting tasks ‣ Canceling async operations ‣ Restricting concurrency ‣ Handling errors and gracefully recovering or retrying ‣ Must-have tool for data-heavy and apps with complex UI interactions export default Component.extend({ hackTheMainframe: task(function* () { const { nmap, metasploit } = this.getProprties('nmap', 'metasploit'); const vulnerableHosts = yield nmap.scanForVulnerableHosts(); console.log( `Found ${vulnerableHosts.length} vulnerable mainframes`); const hackedHosts = yield metasploit.exploitAll(vulnerableHosts); console.log( `Hacked ${hackedHosts.length} mainframes`); return hackedHosts; }).drop().maxConcurrency(3), }); @machty/ember-concurrency
  16. ember-data ‣ de facto standard data management and persistence library

    ‣ 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
  17. ember-redux ‣ Redux bindings for Ember ‣ Works with Redux

    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
  18. ember-simple-auth ‣ de facto standard authentication & session storage add-on

    ‣ 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
  19. ember-composable-helpers ‣ Provides many composable helpers for doing common operations

    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
  20. ember-truth-helpers ‣ Same deal as ember-composable- helpers, but for boolean

    logic operations ‣ eq, not-eq ‣ not, and ‣ or, xor ‣ gt, gte, lt, lte ‣ is-array, is-equal @jmurphyau/ember-truth-helpers ember-cli-string-helpers ‣ Same deal as ember-composable- helpers, but for string operations ‣ camelize, capitalize ‣ titleize, humanize, dasherize ‣ uppercase, lowercase ‣ truncate ‣ etc. @romulomachado/ember-cli-string-helpers
  21. ember-wormhole ‣ Provides component for rendering components to arbitrary spots

    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}}
  22. CLOSING REMARKS React Or Uhh… Try Ember? Computers Only Make

    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
  23. CLOSING REMARKS Shout Outs ▸ Mike North (@mikenorth) - https://mike.works

    ▸ Mike really helped level our team up on Ember ▸ If your team is looking for Ember.js training or training in other web topics, consider hiring Mike