Getting Going With Ember

Created by Landon Noss / @landongn

Who Am I?

I'm Landon

I'm a full stack developer, but my primary focus is on front-end.

I work for Gaikai / Sony Network Entertainment International
(I help build PlayStation Now)

I've been writing ember for about three years.

I've been a javascripter for...ever.

I started out writing bad javascript

then bad jQuery

then bad Backbone

then bad Angular

then bad Ember

then kinda good Ember

The Subjective Stuff:

It's flexible.

It's terse.

It's compatable.

It's written in vanilla ES6.

It's RFC Process.

It's Community

The Super Subjective Stuff:

Working with teams is hard

Ember helps define a grammar

Class types all have the same API

Moving the important state management into the URL is really important

So what even is an Ember?

The most important parts

uhh

well...

Lets talk about a few concepts first

Single Responsibility Principle

Every class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class

- wikipedia.com | Robert C. Martin

Uniform Access Principle

All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation

- wikipedia.com | Bertrand Meyer

So what?

show of hands: who's written this function before?



parseData: function (data) {
  this.name = data.name;
  this.email = data.email;
  this.token = data.token;
  this.fullName = data.firstName + ' ' + data.lastName;
  this.validAccount = data.has_verified ? true : false;
}


what about this?



filterSet: function (key) {
  var ret = [];
  for (var i = 0; i > this.set; i++) {
    if (this.set[i].prop === key) {
      ret.push(this.set[i]);
    }
  }
  return ret;
}


This sucks.

We're so busy writing the how

we sometimes lose sight of the what

How can Ember help us write better code?

The Router

Build lots of explicit states

then build small things that react to that change

The Router is kind of like a rubix cube with callbacks.

When the router detects a change, it kicks off a series of ordered events -- a lifecyle -- that reconfigures all the things.

Defining the Router


// app/router.js
Router.map(function() {

  // defines a standalone route with no substates or arguments
  // routable as 'stateA'
  this.route('stateA');

  // defines a standalone route with no substates and an argument
  // routable as 'stateB'
  this.route('stateB', {path: '/stateB/:someArgument'});

  // defines a route with a child state
  // routable as superState.loading, superState.error,
  // superState.index, superState.childState
  this.route('superState', function () {
  	this.route('childState');
  });

});

Ember conventions expect that names have meaning to the application

since we defined superState in our router

a SuperStateRoute should exist

as well as a SuperStateController

Ember tries to create stubs where it needs

If you dont define a SuperStateController

Ember creates an empty Controller instance for you, at runtime

Now that we have routes defined, how do we implement them?

Ember runs on the backbone of a runloop

the runloop executes a lifecycle of events in a specific order

the order goes a bit like this, for a route class

beforeModel(transition)

model(params)

afterModel()

setupController(controller, model)

renderTemplate()

Routes have a lifecycle whenever state changes

it's your job as an ember developer to fulfill events in that lifecycle

the model hook is all about data

whatever you return from this method is provided to the child controller and templates

controllers, components, and templates use this data to display your UI


// app/route.js
  model: function (params) {
    Ember.$.getJSON('/api/user').then(function (response) {
      return response;
    });
  }

// app/template.hbs

Hi, {{name}}!

Routes & Templates

Templates are the structure your app, via outlets and components.

User events go down the stack, actions go up the stack.

other than components, routes are the only classes that actually render to the dom

Components define sensible chunks of your abstraction

they operate kind of like a template and a view, squished together

there's no reason you shouldn't have a component render another component

or even an entire suite of components

Components are just dom

every component defines a single dom element

it can be as simple or complex as you want it to be

tagName, classNames, attributeBindings are all part of the component spec

defining a component for an image? define it's src property, and it's tagName and you're done

But they don't have to be complex dom

writing component-of-components is encouraged

Controllers are going away in Ember 2.0

Components will take their place once they go away

Handling events is easy with components

  • click()
  • dlbclick()
  • focusin()
  • focusout()
  • touchstart()
  • touchmove()
  • touchend()
  • dragstart()
  • keypress()
  • submit()
  • change()
  • input()

The rule of thumb when handling user interaction:

raw dom events are handled as normal JS events

if those events end up doing something important to the app

then actions are used to propagate that mutation elsewhere

Actions help you define the what.

Actions can be defined on Components, Routes, Controllers and Views

They exist in an actions: {} object in those classes

they're invoked by calling this.send('actionName');

Actions that get invoked but are not defined do magic

rather than throwing an exception, they'll check the immediate parent

if a component calls send, it's templates controller is checked

if that action isn't defined on the controller, it'll check the active route

if it's not defined on the route, it'll check it's parent route, if any

finally, it'll check the application route instance

Actions should be used at an abstraction above raw events.

Actions do one thing.

Actions don't care where the event came from

Actions (on routes) are the only place you can render templates

Actions (on routes) are the only place you can redirect, other than {{link-to}}

My Best Friends are Computed

Computed Properties simplify the act of working with data

Rather than explicitly setting properties for everything in one big method

Define the properties based on the existance of some immutable data

	
//stateA/controller.js [note: does NOT use prototype extensions]
// side note: this is the preferred way to do it.
export default Ember.Controller.extend({

  // define a computed property that updates
  // a slice of an array when it's filtering key changes.
  arraySlice: Ember.computed('filterProp', function (key, value) {

    var thisSlice = [];
    var filter = this.get("filterProp");
    this.get('arrayData').forEach(function (item, iter) {
      if (item.get(filter)) {
        thisSlice.push(item);
      }
    });
    return thisSlice;
  }
});
	
	
//stateA/controller.js [note: does NOT use prototype extensions]
// side note: this is the preferred way to do it.
export default Ember.Object.extend({

  userModel: {},
  fullName: Ember.computed('userModel', function () {
	return this.get('userModel.firstName') + ' ' + this.get('userModel.lastName');
  }),

  hasVerified: Ember.computed('userModel.token', function () {
    return this.get('userModel.token').length >= 32;
  })
});
	

Computed properties are cached

every time a computed is referenced, it's cached value is returned

the only thing that will force an update to the interal property is a change to the observed property

invert the way you define classes through computed properties

treat the internal data that powers them as immutable

There's some great APIs to be found in the computed namespace

  • mapBy
  • filterBy
  • equal
  • match
  • min
  • sum
  • union
  • and
  • empty
  • sort
  • uniq
  • intersect

Code Demo

What Advice would I give to someone starting to learn ember?

Don't fight the framework

It's important to learn the conventions.

Whenever you catch yourself having an argument with ember, you've lost.

Be incredibly liberal with the amount of components you create.

Same goes for routes. You're supposed to build a lot of them.

Observers and Computed Properties make your APIs nice

Think hard about who you're writing this code for. It's not the user.

Make sure the way you design your class adheres to the Universal Access Principle

Rather than parsing data and setting properties, define computed properties that do their own work

The goal is to write maintainable, easy to grok code.

Outlets and how they work are pretty critical.

Outlets define a section of the dom that templates render into

sub-views are trivial, if you have an outlet, since you can nest them recursively

in practice, this means that a route without substates doesn't use an outlet

but a route with substates requires an outlet for children to render to

Ember isn't all about MVC

It's more like a really nuanced state management library

that also has a stellar functional programming library

and super powerful, flexible, and expressive components