The Checklist Detail Page
Now it’s time to create our next smart component/routed component/feature component — whatever you want to call it! This one will be responsible for displaying the details of a particular checklist.
import { Component } from '@angular/core';
@Component({ selector: 'app-checklist', template: ` <p>Hi</p> `,})export default class ChecklistComponent {}Since this is a routed component, we are also going to need to set up the routing:
import { Routes } from '@angular/router';
export const routes: Routes = [ { path: 'home', loadComponent: () => import('./home/home.component'), }, { path: 'checklist/:id', loadComponent: () => import('./checklist/checklist.component'), }, { path: '', redirectTo: 'home', pathMatch: 'full', },];Notice that for this path we are using :id so that we will be able to
retrieve the checklist id passed in as a parameter through the URL. Before we
can do anything with that id we are going to need a way to retrieve
a particular checklist if we have its id.
Getting a single checklist by its id
Let’s add a way to get the specific checklist that matches the id from the
route in our ChecklistComponent.
export default class ChecklistComponent { checklistService = inject(ChecklistService); route = inject(ActivatedRoute);
params = toSignal(this.route.paramMap);
checklist = computed(() => this.checklistService .checklists() .find((checklist) => checklist.id === this.params()?.get('id')) );}We already have an array of all of our checklists available in the state in
our ChecklistService — we want to utilise that here. We also need the id
from the route, so we add the ActivatedRoute and we access its paramMap
observable — we convert this into a signal to make it easier to work with.
Then we create a computed signal called checklist that takes the value of
the checklists signal from our ChecklistService state, and it tries to find
a checklist that matches the id from the route. Now our checklist class
member is a signal that contains the value of the checklist that is currently
being viewed (or undefined if the id in the route does not match any
checklists).
Display the checklist in the template
Eventually we are going to want to add checklist items and view them on this page, but for now we will just focus on displaying the title of the individual checklist.
Rather than just adding it directly to the template of the ChecklistComponent
we are going to create a separate ChecklistHeaderComponent. This is because we
are also going to add add and reset buttons for checklist items into our
header later, and we want to avoid cluttering up our ChecklistComponent
feature.
import { Component, input } from '@angular/core';import { RouterLink } from '@angular/router';import { Checklist } from '../../shared/interfaces/checklist';
@Component({ selector: 'app-checklist-header', template: ` <header> <a routerLink="/home">Back</a> <h1> {{ checklist().title }} </h1> </header> `, imports: [RouterLink],})export class ChecklistHeaderComponent { checklist = input.required<Checklist>();}This allows passing the checklist in as an input and it will just display its
title. We have also added a “back” navigation link here that utilises the
routerLink (which means we also need to add RouterLink to the imports).
Now we can use this component to display the title in our smart component.
@if (checklist(); as checklist){ <app-checklist-header [checklist]="checklist" />}Notice how we have wrapped this in an @if block and created a checklist
alias. This is because checklist() could possibly be undefined. Before
displaying a component that requires a valid Checklist we check if it is
actually defined. Using an alias here lets TypeScript know that we have done
this check and that it is definitely defined.
Navigating to the checklist detail page
Our last step for this lesson will be linking to the checklist detail page from the checklist list component on the home page.
<a routerLink="/checklist/{{ checklist.id }}"> {{ checklist.title }} </a>If we test the application now, we should see that we are able to create a checklist and then click it to go to its detail page!