Angular Router — Feature Modules, Child Routes, and Services
Child routes and per-feature route files keep a growing app's routing config from collapsing into one flat list at the root
Building on Angular Router, today we'll look at feature modules, child routes, redirects, routing modules, and key router services.
Feature modules
With the Day 27 sample app, can we split the app into multiple NgModules instead of one monolithic module — and still use the Router? Yes: use RouterModule.forChild in feature modules.
Extract ArticleModule
Create a module and move related declarations into it:
import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';import { ArticleListComponent } from './article-list/article-list.component';import { ArticleDetailComponent } from './article-detail/article-detail.component';
@NgModule({ imports: [CommonModule], declarations: [ArticleListComponent, ArticleDetailComponent],})export class ArticleModule {}Configure routes with forChild instead of forRoot (see routing intro for why):
import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';import { Routes, RouterModule } from '@angular/router';import { ArticleListComponent } from './article-list/article-list.component';import { ArticleDetailComponent } from './article-detail/article-detail.component';
const routes: Routes = [ { path: 'article', component: ArticleListComponent, }, { path: 'article/:slug', component: ArticleDetailComponent, },];
@NgModule({ imports: [CommonModule, RouterModule.forChild(routes)], declarations: [ArticleListComponent, ArticleDetailComponent],})export class ArticleModule {}Import the feature module in AppModule:
import { ArticleModule } from './article/article.module';
@NgModule({ imports: [ BrowserModule, FormsModule, ArticleModule, // note import order AppRoutingModule, ], declarations: [AppComponent], bootstrap: [AppComponent],})export class AppModule {}Navigate to article to see the list.

Route redirects
Redirect one path to another:
const routes: Routes = [ { path: '', redirectTo: 'article', pathMatch: 'full', },];For redirects, pathMatch: 'full' is usually what you want.
pathMatch strategies
full— the entire URL path must match (like==). User wantstiepphan.com/abc/xyz→ pathabc/xyz;abc/cdedoes not match.prefix(default) — matching prefix is enough. Fortiepphan.com/abc/xyz, pathabcmatches.
Routing module pattern
Split routing into its own module — like AppRoutingModule. For a feature:
const routes: Routes = [ { path: 'article', component: ArticleListComponent, }, { path: 'article/:slug', component: ArticleDetailComponent, },];
@NgModule({ imports: [ CommonModule, RouterModule.forChild(routes), ], declarations: [], exports: [RouterModule],})export class ArticleRoutingModule {}ArticleModule imports ArticleRoutingModule instead of calling RouterModule.forChild directly:
import { ArticleRoutingModule } from './article-routing.module';
@NgModule({ imports: [CommonModule, ArticleRoutingModule], declarations: [ArticleListComponent, ArticleDetailComponent],})export class ArticleModule {}Child routes
These routes share a prefix:
Flat style
const routes: Routes = [ { path: 'article', component: ArticleListComponent, }, { path: 'article/:slug', component: ArticleDetailComponent, },];Parent–child style (equivalent):
const routes: Routes = [ { path: 'article', children: [ { path: '', component: ArticleListComponent, }, { path: ':slug', component: ArticleDetailComponent, }, ], },];A parent route can also activate a layout component that contains a router-outlet for children:
const routes: Routes = [ { path: 'article', component: ArticleComponent, // layout component children: [ { path: '', component: ArticleListComponent, }, { path: ':slug', component: ArticleDetailComponent, }, ], },];ActivatedRoute service
Provides access to information about a route associated with a component that is loaded in an outlet. ActivatedRoute
Use it to read params, query strings, and route data.
Retrieve params
From routing.md — snapshot approach:
export class ArticleDetailComponent implements OnInit { article$: Observable<Article>; constructor(private _route: ActivatedRoute, private _api: ArticleService) {}
ngOnInit(): void { let slug = this._route.snapshot.paramMap.get('slug'); this.article$ = this._api.getArticleBySlug(slug); }}Observable approach (better when the same component instance is reused):
export class ArticleDetailComponent implements OnInit { article$: Observable<Article>; constructor(private _route: ActivatedRoute, private _api: ArticleService) {}
ngOnInit(): void { this.article$ = this._route.paramMap.pipe( map((params) => params.get('slug')), switchMap((slug) => this._api.getArticleBySlug(slug)) ); }}New to RxJS? See the RxJS articles.
Why Observable instead of snapshot?
By default the router tries to reuse a component when configuration matches. Navigating from /article to /article/bai-viet-1 creates a new ArticleDetailComponent — snapshot and paramMap agree.
Navigating from /article/bai-viet-1 to /article/bai-viet-2 reuses the same instance. Snapshot is frozen at creation time; paramMap emits the new slug.
Choose based on whether params can change without recreating the component.


Samples:
- https://stackblitz.com/edit/angular-100-days-of-code-day-28-router-feature-4?file=src/app/article/article-detail/article-detail.component.ts
- https://stackblitz.com/edit/angular-100-days-of-code-day-28-router-feature-5?file=src%2Fapp%2Farticle%2Farticle-detail%2Farticle-detail.component.ts
- https://stackblitz.com/edit/angular-100-days-of-code-day-28-router-feature-6?file=src%2Fapp%2Farticle%2Farticle-detail%2Farticle-detail.component.ts
Query params, route data, and more
Besides paramMap, use queryParamMap for query strings. For URL tiepphan.com/page/2?sort=createdDate:
this._route.snapshot.queryParamMap.get('sort');Or observe:
queryParamMap.subscribe((query) => { console.log(query.get('sort'));});Route data and other APIs are documented on ActivatedRoute.
Router service
A service that provides navigation and URL manipulation capabilities. Router
Navigate programmatically after an action succeeds:
navigateByUrl(url: string | UrlTree, extras: NavigationExtras = { skipLocationChange: false }): Promise<boolean>;navigate(commands: any[], extras: NavigationExtras = { skipLocationChange: false }): Promise<boolean>;class SomeComponent { constructor(private router: Router) {} onClick() { // do something this.router.navigate(['/article']); }}Listen to navigation events:
this.router.events .pipe(filter((e) => e instanceof NavigationEnd)) .subscribe((e) => { console.log(e); });Summary
Feature modules with forChild, redirects, routing modules, child routes, ActivatedRoute, and Router are essential for real Angular apps. Read the official docs and router source when you need deeper detail.