- @if (section === 'components') {
-
![]()
+ @if (section() === 'components') {
+
![]()
}
{{component.name}}
{{component.summary}}
diff --git a/docs/src/app/pages/component-category-list/component-category-list.ts b/docs/src/app/pages/component-category-list/component-category-list.ts
index 9f0588459e0e..cfdb08ecb709 100644
--- a/docs/src/app/pages/component-category-list/component-category-list.ts
+++ b/docs/src/app/pages/component-category-list/component-category-list.ts
@@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {Component, OnDestroy, OnInit, inject} from '@angular/core';
+import {Component, inject, signal} from '@angular/core';
import {ActivatedRoute, RouterLink} from '@angular/router';
import {MatRipple} from '@angular/material/core';
-import {combineLatest, Subscription} from 'rxjs';
+import {combineLatest} from 'rxjs';
import {
DocItem,
@@ -19,6 +19,8 @@ import {
import {NavigationFocus} from '../../shared/navigation-focus/navigation-focus';
import {ComponentPageTitle} from '../page-title/page-title';
+import {map, switchMap} from 'rxjs/operators';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
@Component({
selector: 'app-component-category-list',
@@ -26,33 +28,54 @@ import {ComponentPageTitle} from '../page-title/page-title';
styleUrls: ['./component-category-list.scss'],
imports: [NavigationFocus, RouterLink, MatRipple],
})
-export class ComponentCategoryList implements OnInit, OnDestroy {
+export class ComponentCategoryList {
private readonly _docItems = inject(DocumentationItems);
private readonly _componentPageTitle = inject(ComponentPageTitle);
private readonly _route = inject(ActivatedRoute);
- items: DocItem[] = [];
- section = '';
- routeParamSubscription: Subscription = new Subscription();
- _categoryListSummary: string | undefined;
-
- ngOnInit() {
- this.routeParamSubscription = combineLatest(
- this._route.pathFromRoot.map(route => route.params),
- Object.assign,
- ).subscribe(async params => {
- const sectionName = params['section'];
- const section = SECTIONS[sectionName];
- this._componentPageTitle.title = section.name;
- this._categoryListSummary = section.summary;
- this.section = sectionName;
- this.items = await this._docItems.getItems(sectionName);
- });
- }
+ readonly items = signal
([]);
+ readonly section = signal('');
+ readonly _categoryListSummary = signal(undefined);
+
+ constructor() {
+ // Combine all route parameters from root to current route into a single observable
+ // pathFromRoot gives us the entire route hierarchy (e.g., /docs/:category/:section)
+ combineLatest(this._route.pathFromRoot.map(route => route.params))
+ .pipe(
+ // Merge all parameter objects into one, with child route params overriding parent params
+ // Example: [{category: 'components'}, {section: 'button'}] becomes {category: 'components', section: 'button'}
+ map(paramsArray => paramsArray.reduce((acc, curr) => ({...acc, ...curr}), {})),
+
+ // Switch to a new observable when params change, canceling any pending requests
+ // This prevents race conditions if the user navigates quickly between sections
+ switchMap(params => {
+ // Extract the section name from route parameters
+ const sectionName = params['section'];
+
+ // Look up section metadata from the SECTIONS configuration
+ const section = SECTIONS[sectionName];
+
+ // Update page title in browser tab/window
+ this._componentPageTitle.title = section.name;
+
+ // Update component state with section summary (displayed in UI)
+ this._categoryListSummary.set(section.summary);
+
+ // Store current section name
+ this.section.set(sectionName);
+
+ // Fetch documentation items for this section from the service
+ // switchMap will cancel this request if route params change before completion
+ return this._docItems.getItems(sectionName);
+ }),
- ngOnDestroy() {
- if (this.routeParamSubscription) {
- this.routeParamSubscription.unsubscribe();
- }
+ // Automatically unsubscribe when component is destroyed (no manual cleanup needed)
+ takeUntilDestroyed(),
+ )
+ .subscribe(items => {
+ // Update the items signal with fetched documentation items
+ // This triggers change detection and updates the template
+ this.items.set(items);
+ });
}
}