-
-
Notifications
You must be signed in to change notification settings - Fork 408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add setRouteComponent API #731
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
--- | ||
Stage: Accepted | ||
Start Date: 2021-03-25 | ||
Release Date: Unreleased | ||
Release Versions: | ||
ember-source: vX.Y.Z | ||
ember-data: vX.Y.Z | ||
Relevant Team(s): Framework | ||
RFC PR: https://github.com/emberjs/rfcs/pull/731 | ||
--- | ||
|
||
# Add `setRouteComponent` | ||
|
||
## Summary | ||
|
||
Adds an API for associating a component with a route. When used, the associated | ||
component will be rendered instead of the route's template, allowing Ember | ||
apps to use new features such as template imports and strict mode in whole | ||
applications. | ||
|
||
```js | ||
import Route, { setRouteComponent } from '@ember/routing/route'; | ||
import MyRouteComponent from './component'; | ||
|
||
export default class MyRoute extends Route {} | ||
|
||
setRouteComponent(MyRouteComponent, MyRoute); | ||
``` | ||
|
||
## Motivation | ||
|
||
Recently, Ember added support for strict mode, which enables the usage of | ||
template imports: | ||
|
||
```js | ||
import Component from '@glimmer/component'; | ||
import { setComponentTemplate } from '@ember/component'; | ||
import { precompileTemplate } from '@ember/template-compilation'; | ||
import MyButton from './my-button'; | ||
|
||
export default class Example extends Component {} | ||
|
||
setComponentTemplate( | ||
precompileTemplate( | ||
`<MyButton/>`, | ||
{ | ||
strictMode: true, | ||
scope() { | ||
return { MyButton }; | ||
} | ||
} | ||
), | ||
Example | ||
); | ||
``` | ||
|
||
This example demonstrates how to use the new low level APIs to do this. Clearly, | ||
this isn't the final API that Ember users should write in their apps, as it's | ||
very verbose overall, but it's the first step toward defining a new | ||
programming model which will come together in a future edition. We have, | ||
essentially, entered the pit of incoherence, which means that some of these | ||
features will not be fully complete or ready for mainstream use. Template | ||
imports are such a feature, partially because we don't have a final authoring | ||
format, but also partially because you cannot use them currently with a very | ||
important part of Ember: Routes. | ||
|
||
There currently is not a way to directly set the template of a route the same | ||
way that you can set the template of a component with `setComponentTemplate`. | ||
This means you cannot associate a template defined with `precompileTemplate` | ||
with a route, and so there is no way to use imported values with them. Instead, | ||
components must still be defined in the `app/components` directory, and then | ||
used via resolution in a route's template defined in `app/templates/routes`. | ||
|
||
This RFC seeks to add a method for associating a _component_ with a route. This | ||
will allow users to associate strict mode templates with a route via a | ||
component - either a template only component or one with a backing class. This | ||
will allow the community to continue experimenting with different template | ||
import formats more thoroughly, and will also allow users to experiment with | ||
building Ember apps without controllers by effectively bypassing them. | ||
Controllers will not be fully removed, as they still have some functionality | ||
which has not been replaced, but this will help to see where those gaps are and | ||
figure out what is needed to fill them. | ||
|
||
This takes us one step further into the pit of incoherence, with the goal of | ||
better defining how to move forward in two major areas that we are aiming for in | ||
the next peak of coherence. | ||
|
||
## Detailed design | ||
|
||
This RFC proposes adding the `setRouteComponent` API, importable from | ||
`@ember/routing/route`. | ||
|
||
```js | ||
declare function setRouteComponent(component: Component, route: Route): void; | ||
``` | ||
|
||
Users can associate any component definition with a route via | ||
`setRouteComponent`. When a component has been associated with a route this way, | ||
the route will render this component at its root rather than rendering the route | ||
template defined in `app/templates/routes`. The route's `render` and | ||
`renderTemplate` hooks will also be ignored, and will not be run as they would | ||
have previously. | ||
|
||
The component will be passed the `@model` and `@controller` arguments, which | ||
correspond to the model returned from the route's `model` hook and the instance | ||
of the route's controller. | ||
|
||
This will effectively be syntactic sugar for rendering a top-level component | ||
within a route template today: | ||
|
||
```js | ||
import Route, { setRouteComponent } from '@ember/routing/route'; | ||
import MyRouteComponent from './component'; | ||
|
||
export default class MyRoute extends Route {} | ||
|
||
setRouteComponent(MyRouteComponent, MyRoute); | ||
``` | ||
|
||
Is equivalent to having the following in the `my-route.hbs` file: | ||
|
||
```hbs | ||
<MyRouteComponent @model={{@model}} @controller={{this}} /> | ||
``` | ||
|
||
Assuming that `MyRouteComponent` is resolvable via that name. | ||
|
||
No changes will be made to the route or controller lifecycle, hooks, or | ||
behaviors in general, since this is not related to the goal of unlocking | ||
experimentation with strict mode and route templates, or the goal of | ||
experimenting with controller-less applications. Changes in these areas will be | ||
done in future RFCs instead. | ||
Comment on lines
+171
to
+175
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One unanswered question I thought of here: how does this interact with the existing abilities to muck with specific rendering patterns on routes, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, we should address that. I believe these APIs would be ignored, since we are fully ignoring the route's template in general. Since these APIs are deprecated, I think this also won't introduce any inconsistencies. |
||
|
||
## How we teach this | ||
|
||
For the time being, this API will not be the recommended way of writing Ember | ||
apps. As such, it will not be included in the guides. | ||
Comment on lines
+206
to
+207
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for calling this out |
||
|
||
### API Docs | ||
|
||
`setRouteComponent` can be used to associate a component with a route. When | ||
associated, this component will be rendered instead of the route's template. The | ||
component receives the `@model` and `@controller` arguments, which correspond | ||
to the model returned from the route's `model` hook and the instance of the | ||
route's controller. | ||
|
||
```js | ||
import Route, { setRouteComponent } from '@ember/routing/route'; | ||
import MyRouteComponent from './component'; | ||
|
||
export default class MyRoute extends Route {} | ||
|
||
setRouteComponent(MyRouteComponent, MyRoute); | ||
``` | ||
|
||
Is equivalent to having the following in the `my-route.hbs` file: | ||
|
||
```hbs | ||
<MyRouteComponent @model={{@model}} @controller={{this}} /> | ||
``` | ||
|
||
Assuming that `MyRouteComponent` is resolvable via that name. | ||
|
||
## Drawbacks | ||
|
||
The primary drawback is that this new API increases incoherence in the short | ||
term, since there will now be multiple ways to define templates for routes. | ||
|
||
## Alternatives | ||
|
||
- We could introduce an API for setting the template of a route directly, | ||
`setRouteTemplate`. This would still require users to use controllers as the | ||
backing context for the template, and the API becomes a bit awkward at that | ||
point - should you associate the template with the route, or the controller? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some patterns I have seen is
and
articles.hbs
is just an{{outlet}}
.How strict is this API to only include a
component
? Could handlebars expressions be allowed as the first argument?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whether or not this fits in to the current proposal, I think this is a really cool idea! I've found it useful in the past to be able to have all a route's assets (templates and controllers) come from one JS file.
This is something I've achieved before by overriding the resolver so that it would look for a
template
named export from a route.Being able to use
setRouteComponent
to do something similar would be pretty awesome, even if it required a separatecreateComponent
function to maintain the semantics.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the idea with
ember-template-imports
is that anhbs
expression is a template-only component, and using them together you could definitely do this in practice:If we find that it still makes sense to associate a template that is not a component with a route in the future, then we can add that functionality either via a separate API, or via this API. My hope is that we can start to do the opposite though, lowering the distinction between templates and components even more in the future. I would like, for instance, for
hbs
used in tests to actually be inline-template-only-components, rather than just templates.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Ravenstine note that this is a low-level API on which exactly those kinds of things would be built. In the same way that
setComponentTemplate
allows us to add ember-template-imports syntax on top of a component definition (rather than usingsetComponentTemplate
directly, this API is the necessary building block to allow you to do something similar in the future.Worth note, though, that it wouldn't be exactly like that, because the backing class for a component is not a
Route
. You'd always want to be attaching a component definition, whether that's a template-only component or a class-backed component. With template imports and this API, though, you can easily imagine being able to do something like this, in a hypothetical future where we did add the decorator approach you suggested in the main thread:(Note that I'm not suggesting I want that specifically; just noting that the point is having the appropriate set of primitives that lest us build and experiment with a variety of things in this space.)
As a closely related point, @snewcomer we wouldn't want this to accept strings there, but with strict mode we can straightforwardly use any tool which produces a strict-mode component, so you can imagine just passing
<template>{{outlet}}</template>
there (or the equivalent withhbs
) and it would Just Work™.Edit: to clarify re: "strings", basically what @pzuraq said. We were apparently typing simultaneously. 😂