在 Angular 16 中,withComponentInput 可以將路由資料綁定到Input decorator。相同的功能也適用於signal input。
以下路由資訊可以綁定到Signal Input訊號輸入:
// route-data.routes.ts
import { Route } from '@angular/router';
import { getPerson } from './star-war.api';
export const routeDataRoutes: Route[] = [
{
path: '',
loadComponent: () => import('./home.component'),
},
{
path: 'route-path-param/:id',
loadComponent: () => import('./route-path-param.component'),
data: {
jedi: 'Luke Skywalker',
sith: 'Darth Maul'
},
resolve: {
vader: () => getPerson(4),
}
},
{
path: 'component-input-precedence/:name/p/:id',
loadComponent: () => import('./component-input-precedence.component'),
data: {
name: 'b',
},
resolve: {
name: () => Promise.resolve('c')
}
}
];
routeDataRoutes延遲載入具有路由資料 (route data) 的組件。 route-path-param/:id 路徑載入 RoutePathParamComponent 組件,而 component-input-precedence/:name/p/:id 路徑載入 ComponentInputPrecedenceComponent 組件。
import { Route } from '@angular/router';
import { routeDataRoutes } from './route-data.routes';
export const routes: Route[] = [
{
path: 'home',
loadChildren: () => routeDataRoutes
},
{
path: '',
pathMatch: 'full',
redirectTo: '/home',
},
{
path: '**',
redirectTo: '/home'
}
]
routeDataRoutes 成為 /home 路徑的子路由。
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';
export const appConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
… other providers …
]
}
在provideRouter函數中,第一個參數是路由,第二個參數是withComponentInputBinding函數。
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { RouterOutlet, RouterLink } from '@angular/router';
@Component({
selector: 'app-home',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `
<nav>
<a [routerLink]="['route-path-param', '100']" [queryParams]="queryParams">Lazy-load Route Data</a>
<a [routerLink]="['component-input-precedence', 'a', 'p', '2']" [queryParams]="queryParams2">Component Input Precedence</a>
</nav>
<router-outlet />
`,
})
export default class HomeComponent {
queryParams = {
movie: 'Star War',
director: 'George Lucas'
};
queryParams2 = {
id: '1'
}
}
導覽列新增RouterLink屬性,用於將參數 (Parameters) 和查詢參數 (Query Parameters) 綁定到路徑 (Route Path)。 該組件將 queryParams 和 param 100 綁定到route-path-param 路徑。它還將 queryParams2 和兩個參數 a 和 2 綁定到Component Input Precedence路徑。
{
path: 'route-path-param/:id',
loadComponent: () => import('./route-path-param.component'),
data: {
jedi: 'Luke Skywalker',
sith: 'Darth Maul'
},
resolve: {
vader: () => getPerson(4),
}
},
export default class RoutePathParamComponent {
// path param
id = input();
// query parameters
movie = input<string>();
director = input<string>();
// route data
jedi = input<string>();
// route data
sith = input<string>();
// route resolver
vader = input<Person | undefined>();
}
id 參數映射到 id signal input。 查詢參數 movie和 director 被映射到 movie 和 director signal inputs。路線資料 jedi 和 sith 被映射到 jedi 和 sith signal inputs。 Resolver 呼叫 getPerson 函數並將結果儲存到 vader signal input。
template: `
<app-component-input-container>
<h3>Signal Inputs from activated routes</h3>
<p>Path Param: {{ id() }}</p>
<p class="title">Query Params</p>
<div class="border">
<p>Movie: {{ movie() }}</p>
<p>Director: {{ director() }}</p>
</div>
<p class="title">Route Data</p>
<div class="border">
<p>Jedi: {{ jedi() }}</p>
<p>Sith: {{ sith() }}</p>
</div>
@let evil = vader();
@if (evil) {
<p class="title">Resolved Data</p>
<div class="border">
<p>Name: {{ evil.name }}</p>
<p>Height: {{ evil.height }}</p>
<p>Mass: {{ evil.mass }}</p>
</div>
}
</app-component-input-container>

根據withComponentInputBinding 的來源碼來看,資料 (route data) 優先權最高,參數 (parameter) 次之,查詢參數 (query parameter) 優先權最低。 如果路由資料和查詢參數具有相同的鍵,則 signal input 將與路由資料綁定。
{
path: 'component-input-precedence/:name/p/:id',
loadComponent: () => import('./component-input-precedence.component'),
data: {
name: 'b',
},
resolve: {
name: () => Promise.resolve('c')
}
}
queryParams2 = {
id: '1'
}
export default class ComponentInputPrecedenceComponent {
// path param
id = input();
// resolved data
name = input<string>();
activatedRoute = inject(ActivatedRoute);
queryParamsId = toSignal(this.activatedRoute.queryParamMap.pipe(
map((params) => params.get('id'))), { initialValue: '' });
paramsId = toSignal(this.activatedRoute.paramMap.pipe(
map((params) => params.get('id'))), { initialValue: '' });
paramsName = toSignal(this.activatedRoute.paramMap.pipe(
map((params) => params.get('name'))), { initialValue: '' });
dataName = toSignal(this.activatedRoute.data.pipe(
map((params) => params['name'])), { initialValue: ''});
}
在組件中,我注入ActivatedRoute以從目前路由中提取查詢參數 (query parameter)、參數 (parameter) 和路由資料 (route data)。 queryParamsId、paramsId 和 id 分別為 1、2 和 2。 dataName 和 name 分別是 a 和 c。
<app-component-input-container>
<h3>Component Input Binding Precedence</h3>
<p>Route data precedence: data > param > query param</p>
<p>Query Param Id: {{ queryParamsId() }}, Param Id: {{ paramsId() }}, Input Id: {{ id() }}</p>
<p>Param Id: {{ paramsName() }}, Data: {{ dataName() }}, Input Name: {{ name() }}</p>
</app-component-input-container>

如果您不想啟用 withComponentInputBinding 功能,那麼您可以考慮安裝 ngxtension 函式庫並使用其函數來取得路由資訊 (route data) 作為 signal。
injectParams - 使開發人員能夠從目前路由注入參數作為 signal
injectQueryParams - 使開發人員能夠從目前路由注入查詢參數作為 signal
injectRouteData - 使開發人員能夠將目前路由的資料作為 signal
withComponentInputBinding 功能後,路由參數、查詢參數、資料和解析資料可以綁定到 Signal Input。withComponentInputBinding 功能,可以注入 ActivatedRoute,從目前路由中提取值並使用 toSignal 建立 signal。componentInputBinding的優先權從高到低依序是路由資料、路由參數、路由查詢參數。ngxtension 函式庫 (library) 包含從目前路由 (current route) 注入路由資訊 (route data) 的實用函數 (utility functions)。當開發人員不想啟用 withComponentInputBinding 時,這是一種替代方案。鐵人賽第13天就這樣結束了。