Reacting to Change

Responsibly ...with javascript

Created by Ryan LaBouve / @ryanlabouve

Brought to you by the power of ember!

Reactivity in User Interfaces

"the principle that any changes to application state are reflected immediately to the user, visually or otherwise."

Charles Lowell, Reactive Modeling with Ember

Changes in Application State Reflected Visually

Most Direct Example, A Spreadsheet

Screenshot of Excel

Two Static Properties (A,B) and a Visualized Dyamic Relationship

A more complex example, Color Wheel

Screenshot of Adobe Color CC

Adobe Color CC

This principle of instant feedback is what makes reactive interfaces... so low friction because the consequences of any action are immediately understood. This encourages the user to experiment and probe the relationships between the data in your application. They can “play” in order to verify or invalidate hypotheses.

Charles Lowell, Reactive Modeling with Ember

Given Static Inputs

Visualize Dynamic Relationships

So users can play with data

—Techniques—

Techniques

  • Reactive Primitives
  • Reactive Primitives in Ember
  • Ember.Model
  • Ember.computed

Reactive Primitives

What we're looking for

  • Observable
    (so we can react to changes)
  • Support Dynamic Relationships
    (between static inputs)
  • Run Loop
    (to set everything in motion)

Reactive Primitives In Ember

  • Observable -> Ember.Object
  • Reactive -> Ember.computed
  • Run Loop -> Ember's Runloop (via Backburner.js)

Reach Goals

For the reactive part

  1. Lazy Evaluation
    ( So we are not crunching data before we need it )
  2. Composeable
    ( Keep relationships can reusable and lego-ish )
  3. Testable
    ( Clean encapsulated pieces we can test)
  4. Easy to Reason About

Primitive #1

Observable: Ember.Object

Ember.Object, Setter/Getter


// Setter and Getter Required for Observation
const human = Ember.Object.create({});
human.set('name', 'bob');
human.get('name'); // => "name"
            

Ember.Object, Observable Example


// app/application/route
import Ember from 'ember';

const Human = Ember.Object.extend();

export default Ember.Route.extend({
  model() {
   return Human.create({ firstName: 'bob' });
  }
});
            

{{! app/templates/application.hbs }}
Why, hello there {{model.firstName}}!

First Name: {{input value=model.firstName}}

Ember.Object Running Example

Primitive #2

Reactive: Ember.computed

  • Treat a function like a property
  • Define dependent keys
  • Re-evaluate property if dependent key changes

Ember.Object, Computed Property Example


import Ember from 'ember';
const { computed } = Ember;

const Human = Ember.Object.extend({
  fullName: computed( 'firstName',  'lastName', function() {
      return `${this.get('firstName')} ${this.get('lastName')}`;
   })
});

export default Ember.Route.extend({
  model() {
   return Human.create({ firstName: 'bob', lastName: 'smith' });
  }
});
            

Why, hello there {{model.fullName}}!

First Name: {{input value=model.firstName}}

{{input value=model.lastName}}

Ember.computed Running Example

computed properties

Yey lazy evaluation! Mike North, Compose All the Things

Computed Properties: Marcos

Forming more complex reactive relationships


import Ember from 'ember';
import EmberCPM from 'ember-cpm';

const { Macros: { sum, difference, product }} = EmberCPM;

export default Ember.Component.extend({
  num1: 45,
  num2: 3.5,
  num3: 13.4,
  num4: -2,

  total: sum(
    sum('num1', 'num2', 'num3'),
    difference('num3', 'num2'),
    product(difference('num2', 'num1'), 'num4')
  )
});
						

Computed Properties: Testable

cibernox/ember-cpm, sum-test.js

Computed Properties: Best Practices

With great power...

  • Keep your functions pure
    (Avoid side effects at all costs)
  • Treat dependent keys like parameters
    (If you use it, declare it)
  • Uni-directional data flow is easier
    (make data relationshiops more direct and easier to think about)
  • Dynamic Values should Decorate Static Values
    (or at least that's how I like to think about it)

Practical

We'll start with a human problem

I'm tired of figuring out how much to eat.

Harris Benedict Equation is a formula that uses your BMR and then applies an activity factor to determine your total daily energy expenditure (calories)
Activivty Level Formula
none = BMR x 1.2
light = BMR x 1.375
moderate = BMR x 1.55
intense = BMR x 1.725
ultra brutal = BMR x 1.9
http://www.bmi-calculator.net/bmr-calculator/harris-benedict-equation/
The BMR formula uses the variables of height, weight, age and gender to calculate the Basal Metabolic Rate (BMR)
Women:
BMR = 655 + ( 4.35 x weight, lbs ) + ( 4.7 x height, inches ) - ( 4.7 x age, years )

Men:
BMR = 66 + ( 6.23 x weight, lbs) + ( 12.7 x height, inches ) - ( 6.8 x age, year )
http://www.bmi-calculator.net/bmr-calculator/bmr-formula.php

Human Inputs

  • Weight
  • Height
  • Age
  • Sex

Need to Determine

How much to eat based on amount of activity

Setup our project

https://github.com/ryanlabouve/how-much-to-eat-example


ember new how-much-to-eat
ember install ember-cli-sass
ember install ember-paper
npm install ember-cpm --save-dev

ember generate route application
ember generate controller application
ember serve
            

Create our Human, and Load as Model

Build _VERY_ basic interface

I mean very basic...

Compose all the things to setup BMR

Yey browser REPL

Setup rest of properties

Demo