Skip to content

Instantly share code, notes, and snippets.

@Ravenstine
Created March 26, 2021 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ravenstine/6f907441178b22b4ab513f6c5b947e3f to your computer and use it in GitHub Desktop.
Save Ravenstine/6f907441178b22b4ab513f6c5b947e3f to your computer and use it in GitHub Desktop.
Start Date Relevant Team(s) RFC PR
2020-03-26
Ember.js, Ember CLI

Route Components

Summary

We propose a low-level way to designate a component as the target of a route as an alternative to implicitly rendering a template from the app/templates directory.

Motivation

Today, a route's template file is located in the app/templates directory; with modern components having co-located templates, this results in templates existing under two directories.

In the past, others have requested making components routable. For various reasons, the RFC for routable components is closed. Now that Ember has modernized its syntax and has fully adopted Glimmer components, adding a low-level construct that can make it possible to use a component to render a route should help create a pathway to satisfy the needs of those whom desired routable components.

Alternative to Controllers

We can't talk about routes and components without addressing controllers, which some have long considered to be an oddity in Ember. At present day, controllers have their place, but can be confusing because they live in a space between the behavior of a component and a route. A function called setRouteComponent would allow many apps to get away without controllers if desired.

This isn't to say that setRouteComponent is intended to replace controllers. Rather, it would help the community come to a more conclusive answer on whether controllers are here to stay.

If controllers are done away with one day, it could also lead to fewer concepts that someone new to Ember would have to learn. If it eventually turns out that components can serve the role of controllers, then that's one thing that developers would no longer have to wrap their heads around.

Fewer Directories

Since components can exist as template-only, without a JS file, the ability to use them to render routes is appealing because it can allow the developer to keep their templates together within the same parent directory of app/components. Today, we're stuck having two directories that contain templates: app/components and app/templates. Conceptually, having a single directory related to UI templates should be easier to understand for many developers.

Testing

Allowing route to render in a component may also have the advantage of allowing routes to be integration-tested without the need to boot an entire Ember app or require the setup of a router.

Learning / Interdevelopmental Familiarity

In frontend libraries such as React, components are used in conjunction with a router to render pages – thus supporting similar functionality would allow a workflow familiar to developers coming from other disciplines.

Detailed design

Low-level Primitives

We propose to introduce the following low-level APIs:

setRouteComponent

Similar to setComponentTemplate, the setRouteComponent function takes two arguments, the first being the component class, the second being the route class. It transparently associates the given component class with the route class in a way can be retrieved later with the getRouteComponent function described below. For convenience, setRouteComponent will return the route class (the second argument).

Once a component is associated with a route, the route will no longer lookup a template by name under app/templates.

getRouteComponent

The getRouteComponent function takes a route class and returns the component associated with the given route class, if any, or one of its superclasses, if any, or undefined if no component association was found.

When a component is invoked by a route, the model and controller are passed as arguments.

Rendering to a Component

Basic Example

// app/routes/foo.js
import Route from '@ember/routing/route';
import { setRouteComponent } from 'ember-route-component';
import BarComponent from '../components/bar';

export default FooRoute extends Route {
  model() {
    return this.store.findQuery('user', 123);
  }
}

setRouteComponent(BarComponent, FooRoute);

Decorator Example

Although not specifically called for by this RFC, it might make sense to support decorator syntax in conjunction with plain function-call syntax. This could be worthwhile given the relatively low-cost of implementation.

// app/routes/foo.js
import Route from '@ember/routing/route';
import { setRouteComponent } from 'ember-route-component';
import BarComponent from '../components/bar';

@setRouteComponent(BarComponent)
export default FooRoute extends Route {
  model() {
    return this.store.findQuery('user', 123);
  }
}

Substates

In the case that a developer wants to delegate a component to a loading or error route substate, they would do so the same way they would with a normal route. The obvious difference is that a component delegated to a substate wouldn't receive a model argument.

Note that a substate that uses a component would always need a JS file as well as any files that implement the component, unlike substates that can be a single template file at present. While this is a bit disadvantageous, it's conceivable that one can share a single component between multiple substates.

Open Questions

  • How would the use of {{outlet}} be handled with components?
  • Is this something to be used directly by the end-developer, or should it only be available for the purpose of a higher-level mechanism?
  • How can someone customize arguments that are passed to a component? (e.g. something like a setupComponent route hook) This may determine how underlying aspects of this feature are worked out.
  • Will components be invoked immediately when a route is activated, or should they wait for the model hook?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment