Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Child routes and lazy loading

A child route shows a component inside another page: on a post page the comments open under the text at the address /posts/1/comments. Lazy loading means a page's code is downloaded only on the first visit to it, so the application opens faster.

Child routes

First add a comments method and a PostComment type to PostApi (import it into the service from ./post-comment):

// src/app/post-api.ts
getComments(postId: number) {
  return this.http.get<PostComment[]>(`${this.url}/${postId}/comments`);
}
// src/app/post-comment.ts
export interface PostComment {
  id: number;
  postId: number;
  name: string;
  email: string;
  body: string;
}

Create the component with ng generate component post-comments and describe it as a child route in the children property:

// src/app/app.routes.ts
export const routes: Routes = [
  { path: 'posts', component: PostList },
  {
    path: 'posts/:id',
    component: PostDetail,
    children: [{ path: 'comments', component: PostComments }],
  },
];

A second router-outlet

The child component is rendered in a <router-outlet /> inside the parent. Add one to the post template together with a link:

<!-- src/app/post-detail/post-detail.html -->
@if (post(); as p) {
  <h2>{{ p.title }}</h2>
  <p>{{ p.body }}</p>
  <a routerLink="comments">Show comments</a>
  <router-outlet />
}

Add RouterOutlet to the imports of PostDetail. An address without a leading slash in routerLink is relative to the current page: on /posts/1 the link leads to /posts/1/comments.

The parent's parameter

The id parameter belongs to the parent route, so the child component takes it from parent:

// src/app/post-comments/post-comments.ts
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { PostApi } from '../post-api';
 
@Component({
  selector: 'app-post-comments',
  template: `
    @for (c of comments(); track c.id) {
      <p><b>{{ c.email }}</b>: {{ c.body }}</p>
    }
  `,
})
export class PostComments {
  private id = Number(inject(ActivatedRoute).parent?.snapshot.paramMap.get('id'));
  comments = toSignal(inject(PostApi).getComments(this.id), { initialValue: [] });
}

Click "Show comments" — five comments appear under the post, and the address becomes /posts/1/comments.

Lazy loading a component

Instead of component, give loadComponent — a function with a dynamic import():

// src/app/app.routes.ts
{
  path: 'todos',
  loadComponent: () => import('./todo-list/todo-list').then((m) => m.TodoList),
},

Remove the regular import of TodoList at the top of the file, otherwise it still ends up in the main bundle. ng serve shows a separate Lazy chunk files block in the terminal — that is the page code that is downloaded when you go to /todos.

A lazy group of routes

A whole section with several pages is moved into its own routes file and connected with loadChildren:

// src/app/admin/admin.routes.ts
import { Routes } from '@angular/router';
import { Dashboard } from './dashboard/dashboard';
 
export const adminRoutes: Routes = [
  { path: '', component: Dashboard },
];
// src/app/app.routes.ts
{
  path: 'admin',
  loadChildren: () => import('./admin/admin.routes').then((m) => m.adminRoutes),
},

All pages of the /admin section are loaded as one chunk on the first visit.

Common mistake

Forgetting <router-outlet /> in the parent's template. The address changes to /posts/1/comments, there are no errors, but the comments do not appear — the child component has nowhere to render. If a route has children, its component needs its own router-outlet.

Thanks for reading!

Donate to the site — we work hard for you.