Ember Route Action Helper
A case study in community solutions
@trabus
@jakebixby



Jake Bixby
Senior UI Engineer @

ember-cli core team alumnus
amateur macro photographer
doggo food balancing enthusiast
I have patient puppers
I also take a lot of pictures of little things and flowers
Why am I talking about this?
- I wanted to highlight something I saw in the Ember ecosystem
- I have seen differing opinions of the addon and its use over time
- I have used it and have developed opinions to share
- I needed a topic, and this seemed good?
So what did I see?
- Ember has changed a LOT over the time I've used it
- Ember is adept at spotting good ideas and adopting them
- The process and tooling provided by Ember are pretty unique
- Even in misstep the community generally course corrects
Case study
I am of the opinion that the ember-route-action-helper addon exemplifies how this process is working for the community
Let's take a look at it!
What is it?
- ember-route-action-helper is an addon that was created in preparation for routable components
- It provides a closure action style helper to access route actions
- It completely circumvents the need for controllers in regards to route level actions
export Controller.extend({
actions: {
login(username, password) {
this.send('login', username, password);
}
}
}<button {{action "login" userName password}}>Login</button>Controller
Template
Before
export Route.extend({
actions: {
login(username, password) {
// do login stuff
...
this.transitionTo('home');
}
}
}Route
export Route.extend({
actions: {
login(username, password) {
// do login stuff
...
this.transitionTo('home');
}
}
}<button {{route-action "login" userName password}}>Login</button>
{{yield (route-action "login" userName password)}}Route
Template
After
Why was it created?
- It was intended to be a stop-gap solution for routable components
- It was also intended to simplify the programming model by circumventing controllers entirely
- Applied the closure action pattern to route actions, for currying and passing with a return value
- The existing route and controller action model was confusing and error prone
Action Bubbling

Router Service
" "
Using the new with the old
- The route-action helper leverages the then new router service
- The helper walks through the currently active routes to find an action that matches the name
- The helper returns a function, so the action can be passed/yielded down to components
- Did not interfere with the old model, so adoption could be incremental
What went wrong?
- Routable components were cancelled in January 2018
- When used without restraint, anti-patterns emerged
- When used with restraint, there was still room for poor developer ergonomics
- Still tied heavily to the old pattern of action bubbling
The good
- Providing a way to reach "global" level route actions is actually a good thing
- Avoids use of controllers for actions that should be handled by a route
- Abstracts route level actions as closure actions, removing the need for sendAction or using other bubbling methods from components
The bad
-
Abstracts route level actions as closure actions, sometimes obscuring the original source
- Route "bubbling" means that you have to track your action down sometimes
- Routes intentionally don't hold state, requiring controllerFor or other methods to access state (sometimes misusing services, etc)
- Routes also don't own state, and mutating state where it isn't owned is bad
The ugly
-
Abstracts route level actions as closure actions, allowing naming disconnections
- Use in component templates can cause invisible entanglement, i.e. inside a component 10 levels deep, inside a nested route
- Use inside an addon puts difficult requirements on consumers (setting up a route action, testing setup for anything using it)
- Action name collisions in the route hierarchy can cause issues that are hard to identify
A quick timeline
2015
2019
rwjblue's jsbin
Dec 2015
Dockyard releases addon
Feb 2016
Community adopts
closure actions introduced
June 2015
improved actions rfc
May 2014
routeable components rfc
Feb 2015
routeable components
rfc closed
Jan 2018
Dockyard deprecates addon
Feb 2018
route actions
rfc opened
Oct 2018
route actions
rfc closed
Jan 2019
What now?
- Pre 3.4 continue using controllers and use the route-action helper only when you absolutely need it
- Avoid using it in component templates
- Be mindful of renaming issues
- If you are using 3.6 (or 3.4 + with decorators polyfill) you can use the @action decorator on a service
@action + @service decorators
- Explicitly refer to the action instead of hoping it is somewhere on your route hiearchy
export class Auth extends Service() {
@service router
@action
login(username password) {
// do login stuff
this.router.transitionTo('home');
}
}<Login {{on “click” (fn this.auth.login userName password)}} />export class LoginButton extends Component() {
@service auth
}Simplify the action system
- Use one pattern for actions (instead of three)
- We need to drop the old actions hash system, it carries a lot of cruft
- I think the @action decorator is actually the best path to that. It simply binds context for later use
- Somehow deal with the naming baggage (using the fn helper will help)
Embrace a simpler model
- Drop Controllers entirely (after query params service is a thing)
- Become a service + component framework (maybe with focused routes)
- The @action decorator is an explicit and simple solution
To recap
- ember-route-action-helper is an addon developed by Dockyard in early 2016 to provide a closure action style template helper for using route actions
- It gained popularity in the community because the helper was recommended as a way to prepare for the removal of controllers in Ember
- The pattern inadvertently enabled some anti-patterns
- In early 2018 it was then recommended that the addon not be used after routable components were cancelled
- A better way is here, so use it! (if you can)
But wait!
there's more...
RFCs, Addons, and Evolution
A paragon of community solutions
(A talk within a talk)

Jake Bixby
Senior UI Engineer @

hooman dadman
amateur macro photographer @trabus
doggo food balancing enthusiast @stumpybuns
I definitely have a problem

And I spend entirely too much time crouched around flowers
AMAZING
framework
DEVELOPMENT
has an
for
of itself.
Ember
What are some components
of this meta-framework?
RFC process
Addon system
Emphasis on primitives
Steering committee
Community involvement
Incremental
approach
Experimentation
Deprecations
Release
train
And more!!
Editions
-
RFC process
- Addon system
-
Primitives
- Experimentation
Some key components from the case in the last talk?
What can we extrapolate?
- A gap in the framework was filled by a opt-in, minimally invasive, easily codemodded solution
- Experimentation through an addon exposed unforeseen anti-patterns
- Through a slightly related change in direction, the addon was made less optimal
- The process worked, eventually, which has affected and improved the process by better scoping future candidate solutions
RFCs
- Ember adopted a Request For Comments process from Rust in late 2014
- The RFC process has been instrumental in fleshing out most of modern Ember
- Every single feature of Octane has been brought in through RFCs
- RFCs have also helped identify possible issues with some excellent ideas
RFCs
- Rejected RFCs can still inform and influence ones that get accepted
- Knowing that ideas are discussed and carefully considered before adoption gives me confidence in the framework
- It's become the standard for many projects, including react and vuejs
- They are extremely flexible in application, they are used for every part of the process!
Addons
- Addons are so flexible and powerful that you can do nearly anything with them
- They allow early experimentation and adoption
- They allow incremental adoption of feature sets
-
They help back-port adoption of new features to earlier versions of the app
- They are used inside ember and ember-cli itself!
Primitives
- Ember has always aimed to provide strong primitives on top of JavaScript
- Ember has also aimed to align primitives with native solutions when possible (Promises, Decorators)
- Ember continues to move towards native solutions (classes, decorators) with a provided migration path for each
Evolution
- This cycle of discussion, refinement, experimentation, and adoption, is how Ember has not left any of its users behind despite reinventing itself several times over
- The entirety of the Octane feature-set went through this process, all providing polyfills to ease upgrading
- This is how Ember can take a pre five year old application and bring it all the way up to a canary app
- This is how I believe Ember will continue to be relevant, as it borrows and improves upon good ideas
Thanks!

Thanks!

@trabus
@jakebixby


deck
By Jacob Bixby
deck
- 602