Depending on whether you don't care about the url, want query params, or want the url to be a full-on route there are different directions you can take.
Method 1: No URL State
Turn movie-preview
and movie-detail
into components.
You can use focus
here to your advantage. That way, selecting a different movie preview will un-focus one component and focus another without you needing to update any bindings.
In movie-preview.js
export default Component.extend({
attributeBindings: ['tabindex'],
tabindex: 0,
isFocused: false,
movie: undefined,
focusIn() {
this.set('isFocused', true);
},
focusOut() {
this.set('isFocused', false);
}
});
In the template for movie-preview, we add a conditional.
{{#if isFocused}}
{{movie-detail movie=movie}}
{{/if}}
However, perhaps we want to reflect this state into the URL? Let's start with how we would do this for query params.
Method 2: query param
On the controller for the route, add a query param for selected
.
In the template for this route, we will use eq
(a helper, one possible implementation of which is here) to set isFocused
based on whether the movie for that preview is the currently selected one. We also pass in an action for setting focus.
{{#each movies as |movie|}}
{{movie-preview
movie=movie
isFocused=(eq movie.id selected)
select=(action "selectMovie" movie.id)
}}
{{/each}}
movie-preview.js
will be altered to trigger select on click.
export default Component.extend({
isFocused: false,
movie: undefined,
select: undefined,
click() {
this.sendAction('select');
}
});
Lastly, we'll need to handle this action in the controller for this route.
export default Controller.extend({
queryParams: ['selected'],
selected: undefined,
actions: {
selectMovie(movieId) {
this.set('selected', movieId);
}
}
});
But maybe we wanted this to have a pretty url? This too is easy.
Method 3: Pretty URLs
The first step is to setup nested routes, as you showed.
this.route('movies', {path: '/' }, function() {
this.route('movie-detail', { path: '/:id'});
});
For this, we do need an outlet
as you initially suggested, but we don't need to make that many changes to our code from method 2. Instead of a movie-detail
component, we now have a movie-detail route. Since we didn't need to know the details of this before, I'll skip past that now too. We only care about the mechanics of "activating" the route.
In the template for movie-preview, we change the conditional to wrap an outlet.
{{#if isFocused}}
{{outlet}}
{{/if}}
In the controller, instead of modifying the query param, we need to trigger a transition.
export default Controller.extend({
selected: undefined,
actions: {
selectMovie(movieId) {
this.set('selected', movieId);
this.transitionToRoute('movies.movie-detail', movieId);
},
clearSelection() {
this.set('selected', undefined);
this.transitionToRoute('movies');
}
}
});