EmberConf 2016 – Idiomatic Ember (Speaker Notes)
-
Upload
lauren-elizabeth-tan -
Category
Engineering
-
view
372 -
download
2
Transcript of EmberConf 2016 – Idiomatic Ember (Speaker Notes)
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
IDIOMATIC EMBERFinding the Sweet Spot of Performance & Productivity
·D O C K Y A R D · • hi everyone! • today i want to share some ideas with you about how
we can write idiomatic ember apps that are both
performant and productive
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
bit.ly/idiomatic-ember
• if you can't see the slides clearly, you can look them up
on • bitly idiomatic dash ember (repeat it)
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• but first with a heavy heart, let's address the elephant
in the room – • what's an idiomatic talk without idioms?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• they say time is money • so i'm gonna just cut to the chase
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
🐰 LAUREN TAN ! SUGARPIRATE_ " POTETO
• my name is lauren, and i was born in the year of the
rabbit • you can find me on the interwebs as sugarpirate or
poteto on github
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• when i'm not working on client work or open source, i
sometimes play final fantasy 14 • it's a game that can be quite addictive, but thankfully i
have
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
🐶 ZELDA
• a little puppy Zelda who keeps me grounded in reality • unfortunately she couldn't be here today to give this
talk, so you'll have to listen to me instead
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I WORK AT DOCKYARD
• a year ago i was still living in australia • but i moved to Boston last year to work at DockYard • we work on a ton of Ember and Elixir apps as well very
many open source libraries including ember addons • in 2 days it will be the end of my 1st year at dockyard,
and i have to say that it's been one of the best jobs i've
ever had
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• the best part is, we're hiring senior ember engineers • so please talk to me, brian, marten or estelle later if
you're interested in finding out more
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• and if you come speak to me after my talk, i'll give you
a very limited edition sugarpirate sticker!
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
THE STATE OF JAVASCRIPT IN 2016
• now before we start talking about ember, let's look at
what it's like to build a modern javascript app in the
year 2016
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• react has been steadily gaining ground and
mindshare • and for good reasons • some of its ideas have even made their way into ember
and other frameworks
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• but despite how far we've come from our early days, modern web dev is getting really hard
• i mean, it used to be so simple, right?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• in the Good Old Days™, all you had to do was write some
html, maybe some css, slap some jQuery on a page • and even use a plugin or 5
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• but these days we spend so much time configuring
things • and we spend valuable time writing glue code instead
of working on what matters
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• you have to make a ton of choices for your front end
stack • which can be quite paralyzing
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
leftpad
• & its easy to build a tangled web of dependencies • which can lead to all sorts of problems… • <dramatic pause> 🙃
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
“I hand-rolled my own front-end stack using the most organic, gluten-free
and artisanal micro-libraries.”
🤓
• it's tempting to fall victim to javascript hype • and try to build your own makeshift framework • that might end up slowing you down bc you have to make
sure everything plays nicely together • why go through that pain?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• but that's why we use an opinionated framework like ember, right?
• with ember, we opt into convention over configuration • problems are shared across the community • bc chances are, your app isn't a special snowflake
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Performance Productivity
• these strong standards have given the ember community
hands down the best tooling experience available • we have things like ember-cli, the ember inspector, more
than 2,000 addons, which is an incredible number • and all of this would not have been possible without the
community
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
❤❤
• and i think it's fair to say that ember has one of the most welcoming and friendly communities out there
• our slack channel is always active, and getting help is easy • there are a few people in there that don't seem to sleep • so if you have a question, there will almost always be
someone available to help you out
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
THE EMBER WAY?
• because ember is opinionated, it also means there is
an "ember" way of doing things • instead of rebuilding the wheel and inventing your own
abstractions • we leverage the community's exp in building web apps • with ember 2.0 being released last year in august, quite
a few things have changed since then • changes are main motivation for talk
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Data
Component
Owner
Action
• for example, there has been lots of discussion around
data down, actions up • this is a pattern for one way data flow that we adapted • and is meant to make your apps easier to reason about
& maintain • i'll cover this more in detail later
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• we've also established conventions that singleton
state should live in services instead of controllers • services are excellent for handling the long lived state
of things such as shopping carts, activity feeds and
notification messages
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
this.attrs.fooAction(val);
this.get('fooAction')(val);
VS
• actions have been getting a little confusing lately • with the future introduction of glimmer components • these action calls will be subtly different, although
they may appear to be the same right now
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• we also know that controllers are going away in the
future • i'll talk a little more about what that means and share
some techniques you can use to ease the transition to
routable components
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• in a nutshell, things have changed • and dealing with change can be hard • these changes can be diff to deal with bc • things that used to be best practices may no longer be
true • and lack of updated info about new way of doing
things
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
MORE JAVASCRIPT
• going forward, we can expect to see Ember become
more closely aligned with JavaScript, and for things to
become more explicit and less magical
• ES2017 features and beyond are going to play a pivotal
role in Ember's future
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
DECORATORSStage 1
https://github.com/wycats/javascript-decorators
• for example, you can already use decorators today
using rwjblue's excellent addon
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016https://github.com/rwjblue/ember-computed-decorators
@computed('first', 'last')name(first, last) { return `${first} ${last}`;}
• this is currently a stage 1 proposal, but is incredibly
useful for DRYing up your code
• the most obvious example is with CPs, as you can see
this is much nicer than what we have to do today
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ASYNC/AWAITStage 3
https://github.com/tc39/ecmascript-asyncawait
• async/await functions are also on the horizon, and are
1 stage away from becoming included into the spec
• these let us write asynchronous operations as if they
were synchronous
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
await fillIn('.display-title', 'Hello!');await click('.update-display-title');
assert.equal(find('.display-title').text(), 'Hello!');
https://github.com/emberjs/rfcs/pull/119
• if you ever been trolled by Promises and async
programming, you'll love this new feature
• for example, in rwjblue's RFC for making tests great
again, he describes how we might make use of async/
await to clean up tests
• as a result, code & tests are much easier to read and
follow
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
GENERATORSES2015
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
• generator functions are already a part of es2015
• and are really cool
• they allow you to write co-operative code – you can
pause, resume and restart them as well as end them
early
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
countingTask: task(function* () { this.set('count', 0); while (this.get('count') < 5) { this.incrementProperty('count'); yield timeout(300); } this.set('count', 'DONE!');}).restartable()
https://github.com/machty/ember-concurrency
• these are really powerful, and you should definitely
checkout Machty's addon called ember-concurrency
• it allows you to write Tasks to do things you would have
a hard time replicating without generator functions
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
❤
• as you can see, when you use Ember, you're betting on
JavaScript
• we're going to become even more closely aligned with
the latest JS features, meaning that you'll be able to
write more JavaScript and less Ember
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
– Yehuda Katz
“All good ideas will eventually end up in Ember.”
• i think it's fair to say that ember is the solution to
javascript hype fatigue
• and that is why i'm excited to give this talk, bc with
ember, we don't have to sacrifice performance for
productivity
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
THE EMBER WAY 🙏
• so in this talk, i want to cast away any doubt you might
have about what the ember way really is
• and hopefully you'll take away some useful ideas you
can use straight away in your apps
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• very broadly, i'll be speaking about patterns and anti-
patterns in modern ember apps
• first, i want to talk about an important concept
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• data down actions up is one of the core principles
driving modern ember apps
• by architecting our applications in this manner, we can
build apps that are easier to maintain and reason about
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• data down, actions up describes flow of data thru
app • eg this is what app might look like once routable
components land • fetch data from API, which gets normalized and
pushed into ED store • records then get passed in as attrs to routable
component
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• you might have noticed controllers were missing
from that diagram • and that's because they're going to be deprecated and
removed in the future
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Is MVC Dead? ☠
• but why remove controllers if they already work? • well, core team found that a component could easily
take the place of a controller + view, as components
are superior implementations
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Is MVC Dead? ☠NOPE
• does this mean the death of MVC? • no, but there has been a lot of confusion on this topic,
so i'll address it in more detail later
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
2 WAY BINDINGS 💣
• what React taught us is that the biggest problem of
the frameworks that predate it is the very thing that
made them popular • and that is two way bindings • implicit changes are hard to reason about, and • you can very easily cause an infinite cascade of
changes if you're not careful
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
💎
• when glimmer or angle bracket components land,
data bindings will become one way by default • and instead of mutating data, we will have to send
actions to update it • this becomes a nice, functional way of building
applications that are still reactive • and easier to reason about
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• by ignoring 2 way bindings and instead re-rendering
multiple times efficiently, we can have changes
propagate immediately without introducing all the
cascading semantics of a 2 way bind • and this is made possible with one way data flow and
making renders pure and idempotent
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Who owns the data?
• with DDAU, a guiding principle is to ask who owns
the data • only the owner should be allowed to modify it • this is particularly important when that data is
application state, which we should strive to keep as
single sources of truth
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Component A
Component A.1 Component A.2 Component A.3
Route
Owns the shared state of A1 - A3
• for example, here's a diagram of what a simple ember
app might look like on a given route • lets say Component A only allowed to show 1 child
component at a time • perhaps after doing some config in a child component,
we need to programmatically show the next one • where would the best place be to do that?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Component A
Component A.1 Component A.2 Component A.3
Route
Owns the shared state of A1 - A3
{{component configComponent user=user changeStep=(action "changeStep")}}
• if we follow the DDAU principle, we need to ask
ourselves – "Who owns the data?" • In this case, which config component to render is the
concern of Component A, it owns the shared state of
all its children • so, in order to mutate the current step property, we
should send an action up to Component A
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{component configComponent user=user currentStep=currentStep}}
// don't do this in the child component!
export default Component.extend({ actions: { next() { set(this, 'currentStep', 'configure-foo'); } }});
This is bad ☹
• what we must avoid doing is mutating the
"currentStep" in the child component • it's a subtle difference, but it means an app that is
easier to reason about and maintain • you won't need to spend hours figuring out who is
changing what • because you know there is only one place it is being
changed
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
…or someone else's state!
• but the action is changing the property anyway, why
not just do it directly? • when you change the state that someone else owns,
you're stealing • effectively it means that the owner no longer controls
the data and you have bypassed the owner's interface • however, in the future these problems will likely go
away
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
<configure-username user={{user}} currentStep={{currentStep}}> ...</configure-username>
• Glimmer Components will be 1 way by default • so even if you tried mutating `currentStep` in the child
component, it would not flow up to mutate the
currentStep prop of the parent component • unless you explicitly opt-in to doing so with the `mut`
helper
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Service.extend({ init() { this._super(...arguments); this.data = []; }});
• another thing to note is that services can also own
their own data, • and this is an important part of the Ember
programming model • this will be especially useful to know when routable
components land, • as singleton state must be moved into a service bc
components are stateless
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• with ddau and routable components coming, what do
we do with controllers?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• so, as i mentioned earlier
• controllers aren't completely dead yet…
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ROUTABLE CONTROLLER
• one important thing to note about controllers is that
the concept itself isn't going away
• however, the implementation is changing
• essentially, the concept of a controller decorating a
model still exists, but it is implemented as a routable
component instead
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Controller.extend({ queryParams: ['category'], category: null});
• you might still need to use a controller right now if you
want to use query params and in certain cases of
bubbling actions
• but these will be moved to the route eventually when
routable components land
• that said, don't get too creative in trying to avoid
controllers, only remove when it makes sense
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ROUTABLE CONTROLLER
• Ember 1.x was a straightforward implementation of
MVC, we had a controller and a view
• but we've deprecated views in favor of components
• and it turns out that what the controller + view was
doing could be better handled by a component and
service
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ROUTABLE COMPONENT
• so the routable component was introduced
• the major difference you have to keep in mind is that a
routable component is not a singleton like its controller
counterpart
• but conceptually, they should do the same things,
which is decorate a model
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Component = Controller + View
• a simpler concept is to think of the routable
component as the unification of what we call a
controller and a view
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
<h1>Alpaca Route Template</h1>
<div class="my-amazing-template"> {{#each alpacas as |alpaca|}} <span> I'm a cute little {{alpaca}}! </span> {{/each}}</div>
{{foo-alpaca alpacas=alpacas}}
move to
• there are things you can do today to make this
transition easier
• the first key thing you can do is to move your route-level
template into a top-level component
• when routable components land, it basically just moves
that invocation into the route implicitly
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
// shopping-cart top-level componentexport default Component.extend({ shoppingCart: inject.service(),
actions: { remove(product) { // ... } }});
{{#each shoppingCart.products as |product|}} <h2>{{product.title}}</h2> <p>{{product.description}}</p> <button {{action "remove" product}}>Remove</button>{{/each}}
• the reason it's a good idea to use a top level
component now is that it forces you to separate your
stateful, singleton logic out and into a service
• a transition to a service for the stateful bits and a
component for the stateless bits is most likely to be
future proof
• and will make the transition an easier one
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Controller
Action
Action
Action
Route
Action
Action
Action
move to
• another thing you can do is to move actions that deal
with data to the route
• these actions are likely better located in the route as
that is where they'll eventually live when routable
components land
• however, actions that deal with UI or presentational
logic should remain on the routable component
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ACTIONS 🎬
• there are some gotchas with moving controller actions
to your route, so i'll share a way to do so a little later
• but first, let's take a look at actions
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{foo-bar submit="submit"}}
{{foo-bar click=(action "submit")}}
• currently, actions can be a little confusing
• for example, you could define a classic action on a
component by giving it a string property of the action
name
• or you could use a new style action
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
New-style Actions 👍aka Closure Actions
• as a best practice, you should always prefer to use a
new-style or closure action
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{foo-bar click=(action "submit")}}
• a new-style action is essentially just a regular
javascript function
• which means you can do things like make use of return
values, partially apply arguments and more
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Classic Actions 👎aka String Actions
• while classic or string actions are very implicit in
nature
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{foo-bar submit="submit"}}
• and make things hard to debug
• for example, it would be easy to mistake submit for a
string property
• and it would be difficult to trace where the action
actually lives
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
this.sendAction('someMysteriousAction', args);
• another thing is when you use classic actions you have
to use `sendAction` in your components
• which is kinda like sending your action to space
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Your classic action
• because you send it up somewhere and hope
something is listening to it
• who knows where it's going
• and the worst part is it never comes back with a return
value
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
get(this, 'submit')(...args);
• on the other hand, new-style actions are awesome
• they're one of my favorite additions to ember and
they've been available since 1.13
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• don't let the word new-style or closure action scare
you
• it's really just a regular javascript function
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
actions: { saveAndDoStuff(item) { get(this, 'save')(item) .then((savedItem) => // do stuff); }}
• as i said earlier, you have return values
• these are especially useful when dealing with async
operations like saving a record
• you can return the save promise and then handle it
from inside the component
• e.g. handle success and failure state
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ember install ember-route-action-helper
https://github.com/DockYard/ember-route-action-helper
• so new-style actions are awesome, but out of the box
you can only use them if they're defined on the
controller as they do not bubble like classic actions
• but if you want to move your actions that deal with data
to the route this seems rather lame
• so rwjblue and i built a little addon you can use today
• it's called the ember-route-action-helper
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{foo-bar value=value updateFoo=(route-action "updateFoo")}}
• by using `route-action` in place of `action`
• this lets you have new-style actions that work with
routes, and when the time comes, you can search and
replace route-action back to action
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
SERVICES
• if you recall, controllers without routes can be created,
and it was a common practice to manage singleton
state with them
• a service is a long lived singleton we can use to manage
long lived state
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• a great example for a service is a shopping cart or
activity feed
• for things that have singleton state, you can extract that
logic into a service and then inject it into the objects
you need to
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Service.extend({ init() { this._super(...arguments); this.cart = []; },
addToCart(item) { // ... },
removeFromCart(item) { // ... }});
• a shopping cart is an excellent example of a service,
because there can only be one singleton cart in the
application, and it needs to have long lived state
across the app
• you can implement a cart with as little as setting a
property on a service to be an array
• or you could do more
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• what we must be careful about is not to abuse services
• if your main reason for creating a service is to use it as a
global bucket to avoid passing things around, that's a
bad sign
• instead, adhere to data down actions up and don't use
services for something that doesn't need long lived
singleton state
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• now i want to talk about templating, something i'm
sure is close to all of our hearts
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{if (eq fullName "Jim Bob") "You're the chosen one"}}
https://github.com/jmurphyau/ember-truth-helpers
• another of my fav new things in Ember is the new
Helper impl
• introduced in Ember 1.13 and great for expressing
presentation logic
• for example, this `eq` helper can be found in ember-
truth-helpers addon, which we pretty much use for all
our apps
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{concat "configure-" configName}}
http://emberjs.com/api/classes/Ember.Templates.helpers.html
• Ember itself also ships with a bunch of useful Helpers
like `concat`, `hash` and the`get` helper
• the best thing about Helpers is that it basically lets you
power up your templates
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
import Ember from 'ember';
export function add([a, b]) { return a + b;}
export default Ember.Helper.helper(add);
• again, because Ember is converging towards just being
JavaScript
• a helper is really just a regular JavaScript function
• one of the reasons i like helpers is that it forces you to
keep your functions small and free of side effects
• you can basically create 2 kinds of helpers, this one is a
simple one that will recompute every time the params
change
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Helper.extend({ // ...});
• for more complex needs, you can also make a class
based helper, which is essentially an ember object
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Helper.extend({ localesService: inject.service('locales'), currentLocale: readOnly('localesService.currentLocale'), compute([key]) { let currentLocale = get(this, 'currentLocale'); return get(this, 'localesService').lookup(currentLocale, key); },
localeDidChange: observer('currentLocale', function() { this.recompute(); })});
• this means you can do things like define CPs, use
services and so on
• for example this is a helper that looks up translations
on a locales service
• specifically you might want to listen for a change in
locale
• and then all the translations should auto-update, even
though the locale itself is not passed into the helper
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• some of you might have noticed i used an observer in
my previous example
• class based helpers are currently the only place it is ok
to use one
• bc it is still a lightweight implementation (almost mvp),
and its lifecycle hooks are not implemented yet
• when those become available, we can use them to
recompute instead
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Component.extend({ isDropdownDisplayed: false, actions: { saveUserAndHideDropdown(user) { get(this, 'save')(user) .then((user) => { // do stuff set(this, 'isDropdownDisplayed', false); }); } }});
• another reason i like helpers is because it means a lot
of UI logic can be moved back to where it belongs
• for example, how many times have you written
something like this?
• this feels a little dirty because it mixes data and UI logic
together
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Component.extend({ isDropdownDisplayed: false, actions: { hideDropdown() { set(this, 'isDropdownDisplayed', false); }, saveUser(user) { return get(this, 'save')(user); } }});
• so one thing you could do is to split them up into 2
actions right?
• but now you have a problem, because you can't invoke
them both
• what i'm about to show you demonstrates how
powerful helpers can be
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
<button {{action (pipe saveUser hideDropdown) user}}> Save and Close</button>
• let's say we had a hypothetical helper called `pipe`
• it would let you take the return value from 1 action, and
pass it along to the next one
• and it would keep passing that value down to each
function in the pipe
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
A(B(C(D('E'), 'F'), 'G'), 'H');
• if you've used the programming language elixir before,
this pipe helper is essentially the same as the pipe
operator
• the pipe operator lets you express the above, which
isn't very nice to read, as
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
filing = DB.find_customers |> Orders.for_customers |> sales_tax(2016) |> prepare_filing
E|> D|> C("F")|> B("G")|> A("H")
http://elixir-lang.org/
• a series of data transforms
• this is a lot easier to read compared to the 1st example
• since we use Elixir a lot at DockYard, it was a language
feature we wanted to use in ember
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
<button {{action (pipe addToCart purchase redirectToThankYouPage) item}}> 1-Click Buy</button>
• so the pipe helper was born to let you compose small,
pure actions to allow more declarative templating
• in this e.g. the item argument at the end gets passed
into 1st action, then its return value piped into the rest
• as you can see, helpers can be really powerful tools to
extend templating in your app
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
ember install ember-composable-helpers
https://github.com/DockYard/ember-composable-helpers
• together with my colleague Marten Schilstra, we've
built an addon called ember-composable-helpers
• which contains the pipe helper and other declarative
helpers
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
<button {{action (pipe save closeDropdown) item}}>Save and Close</button><button {{action (pipe save quitApp) item}}>Save and Quit</button>
• the best part about helpers is composability
• helpers can do things that might be more complex to
express in your component
• using the pipe helper example again, you don't need to
define 2 actions called `saveAndClose` and
`saveAndQuit`, you can just compose them directly
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{#if (eq (not (incr (count user))) (decr (count user))))}}
💩
• that said, you shouldn't try to get too ambitious with
nesting helpers inside of each other
• and when your template looks like this, you're better off
creating a CP instead
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• it can be easy to get carried away with using helpers
• how much logic you want to have in your templates
depends on your comfort level
• so use them with caution and exercise best judgement
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• the point is, helpers are better suited for dealing with
UI / presentational logic
• which quite often is highly abstract and unrelated to
the exact data in question
• in contrast to CPs which are coupled to data
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• which brings us to our next question
• when we do need to perform computation, what is the
best way?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
COMPUTED VS HELPER VS COMPONENT HOOKS
• generally speaking, we have 3 ways of doing so
• you could express it as a CP, use a helper in the
template, or use a component hook to set a value
• each approach has its pros and cons, lets look at when
we should prefer to use one over the other
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
COMPUTEDS
• CPs are one of the first things you learn in Ember
• they can range from simple to powerful, and are a nice
way of keeping some computation up to date
• but these auto updates can be double edged sword
• bc observing changes are implicit, so when a CP
recomputes, don't always know why it did
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• the best analogy for a CP is an excel spreadsheet
• it would be painful to sum a1 and b1 by placing event
listeners on the cells and then getting their DOM values
• a CP lets you declaratively express logic that changes
automatically when the dependents change
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Component.extend({ @computed('payment', 'rate', 'periods') annuity(payment, rate, periods) { let factor = ((1 - Math.pow(1 + rate, -periods)) / rate); return payment * factor; }});
• i would recommend using CPs for expressing business
logic
• one of the best things about CPs is that they can
become reusable macros
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export function customMacro(dependentKey, ...keys) { return computed(dependentKey, ...keys, { get() { // computed property logic } });}
• you can extract a CP into a macro by creating a
function that returns a CP
• you can then pass in any arbitrary key or value into the
macro,
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Component.extend({ myValue: customMacro('myKey', 'foo', 'bar'), someValue: customMacro('someKey', 'baz', 'qux'), otherValue: customMacro('otherKey', 'meow', 'woof')});
• and then you can import and use it like you would any
other macro
• this is a great way to DRY up your code and test your
business logic
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• another awesome thing about CPs is that they are
cached
• meaning expensive work is only done when it needs to
be done
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
💪👔👍 Business logic
👍👍👍👍 Reusable
⚛⚛⚛⚛ React to changes
🤔 Changes can be implicit
Totally arbitrary emojis• so basically CPs are great for business logic and have
potential for reuse in other ember objects
• they update when dependents change, but you can
'subscribe' to these explicitly by specifying keys
• however it can be sometimes difficult to trace why a CP
recomputes
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
HELPERS
• helpers can be simple functions or class based
• they don't have a DOM element and are simple to
debug – you know where the data is coming from, and
what comes out of it
• just like CPs, we should strive to keep these pure and
free of side effects
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{#each (repeat 3) as |nil index|}} <div data-thing={{concat "foo-" index}}> {{!some HTML block}} </div>{{/each}}
https://github.com/DockYard/ember-composable-helpers#repeat
• helpers are best suited for generic utility functions,
and are especially useful for UI logic
• for example, this little helper repeats the block inside of
it, 3 times
• it's non-business logic related but helps us DRY up our
templates, so you can use this nice markup instead of
copy pasting a block of HTML 3 times
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
{{capitalize "hello"}} {{capitalize "hello"}}
• another thing to note about helpers is that ember
doesn't guarantee that multiple uses of the helper will
actually run at any given point
• for example, this capitalize helper will only run once,
even though it's used 2x
• this is a good thing as it means ember can optimize
rendering
• but that means helpers must be carefully written to not
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export function toggle([obj, prop]) { return function() { set(obj, prop, !get(obj, prop)); };}
https://github.com/DockYard/ember-composable-helpers#toggle
• you may or may not be surprised to note that helpers
can be used as an action if it returns a function
• again, this is really useful for expressing UI logic, we
might use this toggle helper to toggle a dropdown or a
popover
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
<button {{action (toggle this "isExpanded")}}> {{if isExpanded "I am expanded" "I am not"}}</button>
• you can then use your action helper like this,
• which is much nicer than defining yet another
`toggleIsExpanded` action in your components
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
🎨🖌 Express UI logic
✍❤📖 Composable
😶💭 Simple mental model
🏃 🏃 Can be used as action
🕵🔬🔍 No hooks available (yet)
Totally arbitrary emojis• so helpers are great for UI and presentational logic
• & inherently composable with sub-expr or nested
helpers
• they're easy to understand and can also be used as
actions
• & bc they're still lightweight implementations, we
sometimes have to use observers to recompute
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
COMPONENT HOOKS
• component hooks are also a nice explicit way of
handling computation
• for example, when props change from the outside, we
can handle it easily
• however, they can be quite tricky to use, so you should
prefer a CP unless you know why you need to use a
lifecycle hook
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Component.extend({ didReceiveAttrs() { this.updateChart(get(this, 'data')); },
updateChart(data) { // update the chart's data }});
• lifecycle hooks are best used when you need side
effects, and should be preferred over an observer
• for example, you might have a component that wraps a
chart library which has some kind of render method
• you can use a hook like `didReceiveAttrs` to update the
chart whenever new data flows into the component
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Initial Render
init
didReceiveAttrs
willRender
didInsertElement
didRender
External attr changed
didUpdateAttrs
didReceiveAttrs
willUpdate
willRender
didUpdate
didRender
Internal value changed
willUpdate
willRender
didUpdate
didRender
• this is a simple diagram showing the order of hooks
that fire when components render or re-render
• when using these hooks, its important to think about
whether the changes are 'idempotent'
• in other words, these hooks should work no matter how
many times they are called
• as you cannot easily control the order and timing of the
hook
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
export default Component.extend({ didRender() { // this is an infinite loop that will crash your browser let isFoo = get(this, 'isFoo'); set(this, 'isFoo', !isFoo); }});
• for example, this might look like fairly innocent
• you might want to toggle some property after a
component has rendered
• but because it has side effects, it will cause a re-render,
which fires the `didRender` hook again, which re-
renders, and so on until your browser explodes
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
🐘💨🙊 Dealing with side effects
😭➿😱 Can cause infinite loops
🔨🔨 "Just re-render it"
🚀🚀🚀 Not invoked in FastBoot environment
Totally arbitrary ratings• in a nutshell, lifecycle hooks are useful for replacing
observers and for controlling side effects
• however, they can also be potential footguns, as you
can easily cause an infinite loop or sync issues if your
logic is not idempotent
• so use them with caution
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
SHOULD I USE AN OBSERVER?
• and finally the age old question, should i use an
observer???
• this topic has been covered quite in detail already,
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• but basically, you almost never want to use an
observer
• they're low level primitives used by ember so you don't
have to
• as i mentioned earlier, the only place it is ok to use one
is in a class based helper bc no lifecycle hooks are
implemented yet
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
https://youtu.be/vvZEddrClAQ
• you should remember that every time you use an
observer, Stefan Penner dies a little inside
• so don't do it
• and watch his Wicked Good Ember talk on observers
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
THE FUTURE
• i want to close off the talk by briefly mentioning what
we can learn from game renderers
• and what the future might hold for how we build web
apps
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• game renderers are surprisingly similar to web
applications
• this diagram shows the rendering architecture for
Doom 3
• there is an idea of a "front end" and a "back end" as well
as an Intermediate Representation (IR) that sits
between
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Game world state Front End IR Back End
DirectX / OpenGL
OS
Screen
• the front end captures the state of the world and
determines what contributes to the view
• this is then expressed as an IR, which is then passed
into the backend where it goes through a hardware
abstraction layer like DirectX or OpenGL before
reaching your GPU and then your screen
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Application state Front End HTMLBars Glimmer
DOM APIs
Browser
Screen
• an ember app is similar
• we capture the state of the app and determine what to
render
• our templates go through HTMLBars and Glimmer, and
then to DOM APIs
• this is then taken by your browser and rendered onto
your screen
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• so if games and web apps are so similar
• how can games that are so much more complex and
graphically rich sometimes run faster than a web app?
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Hidden blocks rendered, CPU time wasted
Line of SightCamera
Without occlusion culling
• the answer is that games are heavily optimized to
make use of your GPU
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Hidden blocks not rendered, CPU time saved
Line of SightCamera
With occlusion culling
• & use a bunch of tricks like occlusion culling to
optimize rendering performance
• i'm really excited to see the trailblazing work Chris
Thoburn (@runspired) is doing in this area, you should
follow him for updates
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• in fact, Mozilla is actually working on an experimental
web renderer for Servo, that aims to draw web content
like a modern game engine
• early prototypes are able to render at hundreds of FPS
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
https://air.mozilla.org/bay-area-rust-meetup-february-2016/#@1m53s
• i won't go into further detail, but you can check out the
video for more info
• i think web apps have a lot to learn from game
rendering, and i'm excited to see us moving in that
direction to optimize performance
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
TL;DR
• to summarize
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• we looked at the state of javascript and we saw that
ember is going to closely align itself to new features
from ES2017 and beyond
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• we then looked at what DDAU means and how to
implement it by keeping in mind who the owner of the
data is
• and to avoid mutating data directly
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• then, we cleared the air on controllers
• routable components and services are superior impl
• & will still play the part of a controller, which is to
decorate a model
• we also looked at using ember-route-action-helper for
using route actions in templates
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
I. Data Down, Actions Up?
II. Controllers are Dead
III.Declarative & Composable Templates
• and finally, we looked at ways to power up your
handlebars templates
• helpers are a great way of performing UI logic in a
composable and easily testable way
• you can write your own or use ember-composable-
helpers to make your templates more declarative
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• i want to thank dockyard for allowing me to give this
talk and being an awesome place to work at
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
• we're organizing the WickedGoodEmber conference
this june, so please check out our website for more
info
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
🐰 LAUREN TAN ! SUGARPIRATE_ " POTETO
• once again, please get in touch if you have any
questions
IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY
EMBERCONF 2016
Thanks!
🐰 LAUREN TAN ! SUGARPIRATE_ " POTETO
• remember to speak to me if you're interested in
working AT or WITH DockYard
• and i'll give you a sticker too
• thank you all so much for listening!