compose all the things (wicked good ember 2015)

40
Compose all the things Mike North Wicked Good Ember 2015

Upload: michael-north

Post on 28-Jul-2015

2.202 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Compose all the things (Wicked Good Ember 2015)

Compose all the thingsMike North

Wicked Good Ember 2015

Page 2: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

modernwebui.org

Modern Web UI

advertising.yahoo.com

Yahoo Ads & Data

Hi

ember-resize

ember-orientation

ember-cpm

ember-cli-materialize…and more

Page 3: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Composability

(Mike working w/ a composable system)

Page 4: Compose all the things (Wicked Good Ember 2015)

//TODO

• The state of ember at yahoo • What’s composability, and why do we care? • 4 areas where you can compose today

• Style • CPMs • Components • Tests

Page 5: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Yahoo Ads & Data

• 14 Ember Apps • 68 Ember-focused developers • A “flagship” app that ’s huge (70K lines JS) • An internal collection of add ons

Ember @ Yahoo

Page 6: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Page 7: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Composability

• Recombinant self-contained pieces

• Built around established contracts and conventions

• Promotes reuse

What do I mean?

Page 8: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Why composability?

• Leverage existing code repeatedly • Build apps in a more expressive way (DSLs) • Opportunities for unforeseen uses!

Page 9: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

Style

Tests

Computed Properties

Components

Page 10: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startStyle

Declarative CSS

A<div id=“myThing”> ...</div>

#myThing { float: left; color: white;}

B<div class=“pull-left white-text"> ...</div>

.pull-left { float: left;}

.white-text { color: white;}

Page 11: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startStyle

• Atomic CSS classes • Expressive HTML • Promotes consistency

Declarative CSS

<div class=“pull-left white-text"> ...</div>

.pull-left { float: left;}

.white-text { color: white;}

Page 12: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startStyle

• You may have classes and/or attributes for • Testing • Style • Behavior

Keep attributes & classes organized

<input class="first-name large-input” data-autoid="first-name" />

.large-input { font-size: 32px;}

style

fillIn(‘input[data-autoid="first-name"]','Mike');

testing

Page 13: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

Style

Tests

Computed Properties

Components

Page 14: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComputed Properties

How does a computed property work?

GET Has cached value?

Recalculate

No

Yes

XAllows

caching?

CacheX

XYes

No

ReturnX

Page 15: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComputed Properties

How does a computed property work?

DEPENDENT CHANGED

Cache

X

I’ve changed!

ViewProperty obj.get(‘val’)

Page 16: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComputed Properties

CPs can be thought of as filters

CPrgb

#ff1a99

get() set()

(sometimes)

CPr

b

g#ff1a99

Page 17: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComputed Properties

• Ember.computed.*

Macros make this even easier

function product(prop, coeff) {return Ember.computed(prop, {get() {return this.get(prop) * coeff;

}});

}

Page 18: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComputed Properties

totalAmount: sum( 'subtotal', 'tipAmount', 'taxAmount', product('discount', -1)),

Composable CPs can be mixed and matched

ember-cpm

Page 19: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComputed Properties

Example

Page 20: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

Style

Tests

Computed Properties

Components

Page 21: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

• Not source of truth for state ✔ • Promotes Reuse ✔ • Recombinant ?

Components are pretty close…

ember-cli-materialize

Page 22: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

Looking for this<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

Page 23: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

One option - lowest common element<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

{{#wge-card}} <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action">

{{#wge-card-action}} <span {{action "accept"}}> Accept </span> {{/wge-card-action}} {{#wge-card-action}} <span {{action "cancel"}}> Cancel </span> {{/wge-card-action}}

</div>{{/wge-card}}

Page 24: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

One option - lowest common element<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

{{#wge-card}} <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action">

{{#wge-card-action}} <span {{action "accept"}}> Accept </span> {{/wge-card-action}} {{#wge-card-action}} <span {{action "cancel"}}> Cancel </span> {{/wge-card-action}}

</div>{{/wge-card}}

Not Useful

Page 25: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

Another option - parent does everything<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

{{#wge-cardtitle="Wicked Good Ember"cardActions=myCardActions}}

This will go in the bodyof the card

{{/wge-card}}

Page 26: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

Another option - parent does everything<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

{{#wge-cardtitle="Wicked Good Ember"cardActions=myCardActions}}

This will go in the bodyof the card

{{/wge-card}}Not Compo

sable

Page 27: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

The expressive option

{{#wge-card title="Wicked Good Ember"}} This will go in the body of the card {{#wge-card-action}} <span {{action "accept"}}>Accept</span> {{/wge-card-action}} {{#wge-card-action}} <span {{action "cancel"}}>Cancel</span> {{/wge-card-action}}{{/wge-card}}

Page 28: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

{{#wge-card title="Wicked Good Ember"}}

This will go in the body of the card

{{#wge-card-action}} <span {{action “accept"}}> Accept

</span> {{/wge-card-action}} {{#wge-card-action}} <span {{action “cancel”}}>

Cancel</span>

{{/wge-card-action}}{{/wge-card}}

Components

Content projection - ruh roh<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

4 distinct pieces of content

Page 29: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

{{#wge-card title="Wicked Good Ember"}}

This will go in the body of the card

{{#wge-card-action}} <span {{action “accept"}}> Accept

</span> {{/wge-card-action}} {{#wge-card-action}} <span {{action “cancel”}}>

Cancel</span>

{{/wge-card-action}}{{/wge-card}}

Components

Content projection - ruh roh<div class="card blue-grey darken-1"> <div class="card-content white-text"> <div class="card-title"> Wicked Good Ember </div> This will go in the body of the card </div> <div class="card-action"> <a><span {{action “accept”}}>Accept</span>

</a> <a><span {{action “cancel”}}>Cancel</span>

</a> </div></div>

4 distinct pieces of content

Sub-components project into parent

{{yield}}

Simple property binding

Page 30: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

Content projection approach{{#wge-card

title="Wicked Good Ember"}} This will go in the body of the card

{{#wge-card-action}} <span {{action “accept"}}> Accept

</span> {{/wge-card-action}} {{#wge-card-action}} <span {{action “cancel”}}>

Cancel</span>

{{/wge-card-action}}{{/wge-card}}

• Child components won’t render directly

• Parent will handle rendering of children

• Register/unregister to parent

Page 31: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

Childexport default Ember.Component.extend({

didInsertElement() { this.nearestWithProperty('_wgeCard') .registerWgeAction(this); },

willDestroyElement() { this.nearestWithProperty('_wgeCard') .unregisterWgeAction(this); },

render() {} // Don't render});

const { computed: {alias, empty} } = Ember;

export default Ember.Component.extend({ classNames: ['card'], _wgeCard: true, _cardActions: [],

// Needed to ensure context of // child component templates // is the controller parentController: alias('targetObject'),

registerWgeAction(component) { this.get('_cardActions') .addObject(component); }, unregisterWgeAction(component) { this.get('_cardActions') .removeObject(component); }});

Parent

Components

Page 32: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

import Ember from 'ember';

const { computed: {alias, empty} } = Ember;

export default Ember.Component.extend({ classNames: ['card'], _wgeCard: true, _cardActions: [],

// Needed to ensure context of // child component templates // is the controller parentController: alias('targetObject'),

registerWgeAction(component) { this.get('_cardActions') .addObject(component); },

unregisterWgeAction(component) { this.get('_cardActions') .removeObject(component); }});

Parent

<div class="card-content white-text"> <div class="card-title">{{title}}</div> {{yield}}</div>

{{#if _cardActions.length}} <div class="card-action"> {{#each _cardActions as |cardAction|}} {{view Ember.View tagName='a' template=cardAction.template controller=parentController}} {{/each}} </div>{{/if}}

Components

Parent.hbs

Page 33: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startComponents

<div class="card-content white-text"> <div class="card-title">{{title}}</div> {{yield}}</div>

{{#if _cardActions.length}} <div class="card-action"> {{#each _cardActions as |cardAction|}} {{view Ember.View tagName='a' template=cardAction.template controller=parentController}} {{/each}} </div>{{/if}}

“Captured” template

Needed for actions & bindings

Page 34: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to start

Style

Tests

Computed Properties

Components

Page 35: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startTests

A lot of tests are verbose and ugly

• Sensitivity to order and/or timing • Brittle selectors to interact with the DOM • 1 change —> break N tests

Page 36: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startTests

test(“authorized user should end up at account's search list page”, function(assert) { server.get(`${apiHost.url}/me`, json(200, me)); server.get(`${apiHost.url}/campaigns/:id`, json(200, campaign1)); server.get(`${apiHost.url}/seats/2`, json(200, seat2)); server.get(`${apiHost.url}/seats/1`, json(200, seat1)); server.get(`${apiHost.url}/account/:id`, json(200, account1)); server.get(`${apiHost.url}/breadcrumbs`, json(200, breadcrumbs.campaign));

visit('/app/account/1/campaigns');

andThen(function() { assert.equal(currentPath(), ‘app.account.campaign', 'Current url is search list page for campaign 1'); assert.equal(Ember.$('.top-navbar .brand-logo .active-title').text().trim(), campaign1.campaign.name, 'Campaign name header is on the page'); assert.equal(Ember.$('.resource-tiles-container .card').length, campaigns.campaigns.length, 'One row in the table per search'); assert.deepEqual(Ember.$('.resource-tiles-container .resource-tile:first-child .card .card-content-row .card-content-row-label').toArray().map(e => Ember.$(e).text()), ['Created', 'Updated'], 'Columns are correct'); assert.equal(Ember.$('.new-campaign-button').length, 1, 'New Campaign button is on the screen'); });});

A lot of tests are verbose and ugly

Page 37: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startTests

A wild PageObject appears

• Prime pretender • Access to controls • Specific asserts

fillInclick

currentURL

andThenvisit

triggerEvent

$().val()$

$().click()$().trigger()

setFirstName

clickResetButton setAge

openSettings

PageObject API

Ember Testing API

DOMSee:

Page 38: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startTests

Writing PageObjects

• Return this • To assert or not to assert? • Build PageObjects for components

ExampleThe recombinant part!

Page 39: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Great places to startSome final thoughts

Even more composability on the way!

• Add-ons • Ember.Service • Engines (TBD) • Components ( {{yield}}, block params,

etc…)

Page 40: Compose all the things (Wicked Good Ember 2015)

@MichaelLNorth

Conclusion

Style

Tests

Computed Properties

Components

truenorth/wge-examples