SlideShare ist ein Scribd-Unternehmen logo
1 von 40
Downloaden Sie, um offline zu lesen
Compose all the things
Mike North
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
@MichaelLNorth
Composability
(Mike working w/ a composable system)
//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
@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
@MichaelLNorth
@MichaelLNorth
Composability
• Recombinant self-
contained pieces
• Built around
established contracts
and conventions
• Promotes reuse
What do I mean?
@MichaelLNorth
Why composability?
• Leverage existing code repeatedly
• Build apps in a more expressive way (DSLs)
• Opportunities for unforeseen uses!
@MichaelLNorth
Great places to start
Style
Tests
Computed Properties
Components
@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;
}
@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;
}
@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
@MichaelLNorth
Great places to start
Style
Tests
Computed Properties
Components
@MichaelLNorth
Great places to startComputed Properties
How does a computed property work?
GET
Has cached
value?
Recalculate
No
Yes
X
Allows
caching?
Cache
X
XYes
No
ReturnX
@MichaelLNorth
Great places to startComputed Properties
How does a computed property work?
DEPENDENT CHANGED
Cache
X
I’ve
changed!
ViewProperty
obj.get(‘val’)
@MichaelLNorth
Great places to startComputed Properties
CPs can be thought of as filters
CP
r
g
b
#ff1a99
get() set()
(sometimes)
CP
r
b
g
#ff1a99
@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;
}
});
}
@MichaelLNorth
Great places to startComputed Properties
totalAmount: sum(
'subtotal',
'tipAmount',
'taxAmount',
product('discount', -1)
),
Composable CPs can be mixed and
matched
ember-cpm
@MichaelLNorth
Great places to startComputed Properties
Example
@MichaelLNorth
Great places to start
Style
Tests
Computed Properties
Components
@MichaelLNorth
Great places to startComponents
• Not source of truth for state ✔
• Promotes Reuse ✔
• Recombinant ?
Components are pretty close…
ember-cli-materialize
@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>
@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}}
@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
@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-card
title="Wicked Good Ember"
cardActions=myCardActions}}
This will go in the body
of the card
{{/wge-card}}
@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-card
title="Wicked Good Ember"
cardActions=myCardActions}}
This will go in the body
of the card
{{/wge-card}}
Not Composable
@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}}
@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
@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
@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
@MichaelLNorth
Great places to start
Child
export 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
@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
@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
@MichaelLNorth
Great places to start
Style
Tests
Computed Properties
Components
@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
@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 campaig
assert.equal(Ember.$('.top-navbar .brand-logo .active-title').text().trim(), campaign1.campaign
assert.equal(Ember.$('.resource-tiles-container .card').length, campaigns.campaigns.length, 'On
assert.deepEqual(Ember.$('.resource-tiles-container .resource-tile:first-child .card .card-cont
['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
@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:
@MichaelLNorth
Great places to startTests
Writing PageObjects
• Return this
• To assert or not to assert?
• Build PageObjects for components
Example
The recombinant
part!
@MichaelLNorth
Great places to startSome final thoughts
Even more composability on the way!
• Add-ons
• Ember.Service
• Engines (TBD)
• Components ( {{yield}}, block params,
etc…)
@MichaelLNorth
Conclusion
Style
Tests
Computed Properties
Components
truenorth/wge-examples

Weitere ähnliche Inhalte

Ähnlich wie Compose all the things (Wicked Good Ember 2015)

TOSSUG HTML5 讀書會 新標籤與表單
TOSSUG HTML5 讀書會 新標籤與表單TOSSUG HTML5 讀書會 新標籤與表單
TOSSUG HTML5 讀書會 新標籤與表單偉格 高
 
Frontend for developers
Frontend for developersFrontend for developers
Frontend for developersHernan Mammana
 
The Enterprise Architecture You Always Wanted
The Enterprise Architecture You Always WantedThe Enterprise Architecture You Always Wanted
The Enterprise Architecture You Always WantedThoughtworks
 
The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...
The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...
The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...Thoughtworks
 
How We Localize & Mobilize WP Sites - Pubcon 2013
How We Localize & Mobilize WP Sites - Pubcon 2013How We Localize & Mobilize WP Sites - Pubcon 2013
How We Localize & Mobilize WP Sites - Pubcon 2013Search Commander, Inc.
 
Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5
Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5
Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5Cengage Learning
 
Introduction to web components
Introduction to web componentsIntroduction to web components
Introduction to web componentsMarc Bächinger
 
20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile Board20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile BoardMarcus Hammarberg
 
Get the Look and Feel You Want in Oracle APEX
Get the Look and Feel You Want in Oracle APEXGet the Look and Feel You Want in Oracle APEX
Get the Look and Feel You Want in Oracle APEXJorge Rimblas
 
Performance as User Experience [AEADC 2018]
Performance as User Experience [AEADC 2018]Performance as User Experience [AEADC 2018]
Performance as User Experience [AEADC 2018]Aaron Gustafson
 
Basic HTML CSS Slides
Basic HTML CSS Slides Basic HTML CSS Slides
Basic HTML CSS Slides Allyson Wehrs
 
The Offspring of SEO and Semantic Web: SEO++
The Offspring of SEO  and Semantic Web: SEO++ The Offspring of SEO  and Semantic Web: SEO++
The Offspring of SEO and Semantic Web: SEO++ Jay Myers
 
Polymer Polytechnic George Town 2014
Polymer Polytechnic George Town 2014Polymer Polytechnic George Town 2014
Polymer Polytechnic George Town 2014Vin Lim
 
Does my DIV look big in this?
Does my DIV look big in this?Does my DIV look big in this?
Does my DIV look big in this?glen_a_smith
 
Enough with the javas cript already! de Nicholas Zakas
Enough with the javas cript already! de Nicholas ZakasEnough with the javas cript already! de Nicholas Zakas
Enough with the javas cript already! de Nicholas ZakasKubide
 
How to Prepare a WordPress Theme for Public Release
How to Prepare a WordPress Theme for Public ReleaseHow to Prepare a WordPress Theme for Public Release
How to Prepare a WordPress Theme for Public ReleaseDavid Yeiser
 
Getting the most out of Radiant
Getting the most out of RadiantGetting the most out of Radiant
Getting the most out of Radiantjomz83
 
Progressively Enhancing WordPress Themes
Progressively Enhancing WordPress ThemesProgressively Enhancing WordPress Themes
Progressively Enhancing WordPress ThemesDigitally
 
Enough with the JavaScript already!
Enough with the JavaScript already!Enough with the JavaScript already!
Enough with the JavaScript already!Nicholas Zakas
 

Ähnlich wie Compose all the things (Wicked Good Ember 2015) (20)

TOSSUG HTML5 讀書會 新標籤與表單
TOSSUG HTML5 讀書會 新標籤與表單TOSSUG HTML5 讀書會 新標籤與表單
TOSSUG HTML5 讀書會 新標籤與表單
 
Frontend for developers
Frontend for developersFrontend for developers
Frontend for developers
 
The Enterprise Architecture You Always Wanted
The Enterprise Architecture You Always WantedThe Enterprise Architecture You Always Wanted
The Enterprise Architecture You Always Wanted
 
The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...
The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...
The Enterprise Architecture you always wanted: A Billion Transactions Per Mon...
 
How We Localize & Mobilize WP Sites - Pubcon 2013
How We Localize & Mobilize WP Sites - Pubcon 2013How We Localize & Mobilize WP Sites - Pubcon 2013
How We Localize & Mobilize WP Sites - Pubcon 2013
 
Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5
Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5
Course Tech 2013, Sasha Vodnik, A Crash Course in HTML5
 
Introduction to web components
Introduction to web componentsIntroduction to web components
Introduction to web components
 
20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile Board20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile Board
 
Get the Look and Feel You Want in Oracle APEX
Get the Look and Feel You Want in Oracle APEXGet the Look and Feel You Want in Oracle APEX
Get the Look and Feel You Want in Oracle APEX
 
Performance as User Experience [AEADC 2018]
Performance as User Experience [AEADC 2018]Performance as User Experience [AEADC 2018]
Performance as User Experience [AEADC 2018]
 
Basic HTML CSS Slides
Basic HTML CSS Slides Basic HTML CSS Slides
Basic HTML CSS Slides
 
The Offspring of SEO and Semantic Web: SEO++
The Offspring of SEO  and Semantic Web: SEO++ The Offspring of SEO  and Semantic Web: SEO++
The Offspring of SEO and Semantic Web: SEO++
 
Polymer Polytechnic George Town 2014
Polymer Polytechnic George Town 2014Polymer Polytechnic George Town 2014
Polymer Polytechnic George Town 2014
 
Does my DIV look big in this?
Does my DIV look big in this?Does my DIV look big in this?
Does my DIV look big in this?
 
Enough with the javas cript already! de Nicholas Zakas
Enough with the javas cript already! de Nicholas ZakasEnough with the javas cript already! de Nicholas Zakas
Enough with the javas cript already! de Nicholas Zakas
 
How to Prepare a WordPress Theme for Public Release
How to Prepare a WordPress Theme for Public ReleaseHow to Prepare a WordPress Theme for Public Release
How to Prepare a WordPress Theme for Public Release
 
Getting the most out of Radiant
Getting the most out of RadiantGetting the most out of Radiant
Getting the most out of Radiant
 
Progressively Enhancing WordPress Themes
Progressively Enhancing WordPress ThemesProgressively Enhancing WordPress Themes
Progressively Enhancing WordPress Themes
 
Enough with the JavaScript already!
Enough with the JavaScript already!Enough with the JavaScript already!
Enough with the JavaScript already!
 
Please dont touch-3.6-jsday
Please dont touch-3.6-jsdayPlease dont touch-3.6-jsday
Please dont touch-3.6-jsday
 

Mehr von Mike North

Web Security: A Primer for Developers
Web Security: A Primer for DevelopersWeb Security: A Primer for Developers
Web Security: A Primer for DevelopersMike North
 
A Debugging Adventure: Journey through Ember.js Glue
A Debugging Adventure: Journey through Ember.js GlueA Debugging Adventure: Journey through Ember.js Glue
A Debugging Adventure: Journey through Ember.js GlueMike North
 
Anatomy of a Progressive Web App
Anatomy of a Progressive Web AppAnatomy of a Progressive Web App
Anatomy of a Progressive Web AppMike North
 
Web and Native: Bridging the Gap
Web and Native: Bridging the GapWeb and Native: Bridging the Gap
Web and Native: Bridging the GapMike North
 
The Road to Native Web Components
The Road to Native Web ComponentsThe Road to Native Web Components
The Road to Native Web ComponentsMike North
 
Enemy of the state
Enemy of the stateEnemy of the state
Enemy of the stateMike North
 
Phoenix for Rubyists - Rubyconf Brazil 2016
Phoenix for Rubyists - Rubyconf Brazil 2016Phoenix for Rubyists - Rubyconf Brazil 2016
Phoenix for Rubyists - Rubyconf Brazil 2016Mike North
 
Phoenix for Rubyists
Phoenix for RubyistsPhoenix for Rubyists
Phoenix for RubyistsMike North
 
Write Once, Run Everywhere - Ember.js Munich
Write Once, Run Everywhere - Ember.js MunichWrite Once, Run Everywhere - Ember.js Munich
Write Once, Run Everywhere - Ember.js MunichMike North
 
Delightful UX for Distributed Systems
Delightful UX for Distributed SystemsDelightful UX for Distributed Systems
Delightful UX for Distributed SystemsMike North
 
Modern, Scalable, Ambitious apps with Ember.js
Modern, Scalable, Ambitious apps with Ember.jsModern, Scalable, Ambitious apps with Ember.js
Modern, Scalable, Ambitious apps with Ember.jsMike North
 
Ember addons, served three ways
Ember addons, served three waysEmber addons, served three ways
Ember addons, served three waysMike North
 
CI/CD and Asset Serving for Single Page Apps
CI/CD and Asset Serving for Single Page AppsCI/CD and Asset Serving for Single Page Apps
CI/CD and Asset Serving for Single Page AppsMike North
 
User percieved performance
User percieved performanceUser percieved performance
User percieved performanceMike North
 
User-percieved performance
User-percieved performanceUser-percieved performance
User-percieved performanceMike North
 
Write Once, Run Everywhere
Write Once, Run EverywhereWrite Once, Run Everywhere
Write Once, Run EverywhereMike North
 
Async JavaScript in ES7
Async JavaScript in ES7Async JavaScript in ES7
Async JavaScript in ES7Mike North
 
Test like a pro with Ember.js
Test like a pro with Ember.jsTest like a pro with Ember.js
Test like a pro with Ember.jsMike North
 
Modern Web UI - Web components
Modern Web UI - Web componentsModern Web UI - Web components
Modern Web UI - Web componentsMike North
 

Mehr von Mike North (19)

Web Security: A Primer for Developers
Web Security: A Primer for DevelopersWeb Security: A Primer for Developers
Web Security: A Primer for Developers
 
A Debugging Adventure: Journey through Ember.js Glue
A Debugging Adventure: Journey through Ember.js GlueA Debugging Adventure: Journey through Ember.js Glue
A Debugging Adventure: Journey through Ember.js Glue
 
Anatomy of a Progressive Web App
Anatomy of a Progressive Web AppAnatomy of a Progressive Web App
Anatomy of a Progressive Web App
 
Web and Native: Bridging the Gap
Web and Native: Bridging the GapWeb and Native: Bridging the Gap
Web and Native: Bridging the Gap
 
The Road to Native Web Components
The Road to Native Web ComponentsThe Road to Native Web Components
The Road to Native Web Components
 
Enemy of the state
Enemy of the stateEnemy of the state
Enemy of the state
 
Phoenix for Rubyists - Rubyconf Brazil 2016
Phoenix for Rubyists - Rubyconf Brazil 2016Phoenix for Rubyists - Rubyconf Brazil 2016
Phoenix for Rubyists - Rubyconf Brazil 2016
 
Phoenix for Rubyists
Phoenix for RubyistsPhoenix for Rubyists
Phoenix for Rubyists
 
Write Once, Run Everywhere - Ember.js Munich
Write Once, Run Everywhere - Ember.js MunichWrite Once, Run Everywhere - Ember.js Munich
Write Once, Run Everywhere - Ember.js Munich
 
Delightful UX for Distributed Systems
Delightful UX for Distributed SystemsDelightful UX for Distributed Systems
Delightful UX for Distributed Systems
 
Modern, Scalable, Ambitious apps with Ember.js
Modern, Scalable, Ambitious apps with Ember.jsModern, Scalable, Ambitious apps with Ember.js
Modern, Scalable, Ambitious apps with Ember.js
 
Ember addons, served three ways
Ember addons, served three waysEmber addons, served three ways
Ember addons, served three ways
 
CI/CD and Asset Serving for Single Page Apps
CI/CD and Asset Serving for Single Page AppsCI/CD and Asset Serving for Single Page Apps
CI/CD and Asset Serving for Single Page Apps
 
User percieved performance
User percieved performanceUser percieved performance
User percieved performance
 
User-percieved performance
User-percieved performanceUser-percieved performance
User-percieved performance
 
Write Once, Run Everywhere
Write Once, Run EverywhereWrite Once, Run Everywhere
Write Once, Run Everywhere
 
Async JavaScript in ES7
Async JavaScript in ES7Async JavaScript in ES7
Async JavaScript in ES7
 
Test like a pro with Ember.js
Test like a pro with Ember.jsTest like a pro with Ember.js
Test like a pro with Ember.js
 
Modern Web UI - Web components
Modern Web UI - Web componentsModern Web UI - Web components
Modern Web UI - Web components
 

Kürzlich hochgeladen

Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Zilliz
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Victor Rentea
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024The Digital Insurer
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfOverkill Security
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Victor Rentea
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...apidays
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...apidays
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Jeffrey Haguewood
 

Kürzlich hochgeladen (20)

Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 

Compose all the things (Wicked Good Ember 2015)

  • 1. Compose all the things Mike North Wicked Good Ember 2015
  • 2. @MichaelLNorth modernwebui.org Modern Web UI advertising.yahoo.com Yahoo Ads & Data Hi ember-resize ember-orientation ember-cpm ember-cli-materialize …and more
  • 4. //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
  • 5. @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
  • 7. @MichaelLNorth Composability • Recombinant self- contained pieces • Built around established contracts and conventions • Promotes reuse What do I mean?
  • 8. @MichaelLNorth Why composability? • Leverage existing code repeatedly • Build apps in a more expressive way (DSLs) • Opportunities for unforeseen uses!
  • 9. @MichaelLNorth Great places to start Style Tests Computed Properties Components
  • 10. @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; }
  • 11. @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; }
  • 12. @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
  • 13. @MichaelLNorth Great places to start Style Tests Computed Properties Components
  • 14. @MichaelLNorth Great places to startComputed Properties How does a computed property work? GET Has cached value? Recalculate No Yes X Allows caching? Cache X XYes No ReturnX
  • 15. @MichaelLNorth Great places to startComputed Properties How does a computed property work? DEPENDENT CHANGED Cache X I’ve changed! ViewProperty obj.get(‘val’)
  • 16. @MichaelLNorth Great places to startComputed Properties CPs can be thought of as filters CP r g b #ff1a99 get() set() (sometimes) CP r b g #ff1a99
  • 17. @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; } }); }
  • 18. @MichaelLNorth Great places to startComputed Properties totalAmount: sum( 'subtotal', 'tipAmount', 'taxAmount', product('discount', -1) ), Composable CPs can be mixed and matched ember-cpm
  • 19. @MichaelLNorth Great places to startComputed Properties Example
  • 20. @MichaelLNorth Great places to start Style Tests Computed Properties Components
  • 21. @MichaelLNorth Great places to startComponents • Not source of truth for state ✔ • Promotes Reuse ✔ • Recombinant ? Components are pretty close… ember-cli-materialize
  • 22. @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>
  • 23. @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}}
  • 24. @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
  • 25. @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-card title="Wicked Good Ember" cardActions=myCardActions}} This will go in the body of the card {{/wge-card}}
  • 26. @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-card title="Wicked Good Ember" cardActions=myCardActions}} This will go in the body of the card {{/wge-card}} Not Composable
  • 27. @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}}
  • 28. @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
  • 29. @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
  • 30. @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
  • 31. @MichaelLNorth Great places to start Child export 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
  • 32. @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
  • 33. @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
  • 34. @MichaelLNorth Great places to start Style Tests Computed Properties Components
  • 35. @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
  • 36. @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 campaig assert.equal(Ember.$('.top-navbar .brand-logo .active-title').text().trim(), campaign1.campaign assert.equal(Ember.$('.resource-tiles-container .card').length, campaigns.campaigns.length, 'On assert.deepEqual(Ember.$('.resource-tiles-container .resource-tile:first-child .card .card-cont ['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
  • 37. @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:
  • 38. @MichaelLNorth Great places to startTests Writing PageObjects • Return this • To assert or not to assert? • Build PageObjects for components Example The recombinant part!
  • 39. @MichaelLNorth Great places to startSome final thoughts Even more composability on the way! • Add-ons • Ember.Service • Engines (TBD) • Components ( {{yield}}, block params, etc…)