必需的訊號輸入不能在建構函式或欄位初始值設定項中使用,因為該值不可用。要存取該值,我的解決方案是觀察訊號的實際變化,向伺服器發出 HTTP 請求,然後設定訊號的值。關於不使用該效果的討論有很多,我必須找到其他解決方案來刪除它。
所需的訊號輸入可以在 ngOnInit 和 ngOnChanges 生命週期方法中存取。但是,toSignal 會在其中引發錯誤,因為它們位於注入上下文之外。可以用兩種方式修復:
import { Component, effect, inject, Injector, input, signal } from '@angular/core'; import { getPerson, Person } from './star-war.api'; import { StarWarPersonComponent } from './star-war-person.component'; @Component({ selector: 'app-star-war', standalone: true, imports: [StarWarPersonComponent], template: ` <p>Jedi Id: {{ jedi() }}</p> <app-star-war-person [person]="fighter()" kind="Jedi Fighter" />`, }) export class StarWarComponent { // required signal input jedi = input.required<number>(); injector = inject(Injector); fighter = signal<Person | undefined>(undefined); constructor() { effect((OnCleanup) => { const sub = getPerson(this.jedi(), this.injector) .subscribe((result) => this.fighter.set(result)); OnCleanup(() => sub.unsubscribe()); }); } }
程式碼更改如下:
export type Person = { name: string; height: string; mass: string; hair_color: string; skin_color: string; eye_color: string; gender: string; films: string[]; }
import { HttpClient } from "@angular/common/http"; import { inject, Injectable } from "@angular/core"; import { catchError, Observable, of, tap } from "rxjs"; import { Person } from "./person.type"; const URL = 'https://swapi.dev/api/people'; @Injectable({ providedIn: 'root' }) export class StarWarService { private readonly http = inject(HttpClient); getData(id: number): Observable<Person | undefined> { return this.http.get<Person>(`${URL}/${id}`).pipe( tap((data) => console.log('data', data)), catchError((err) => { console.error(err); return of(undefined); })); } }
建立一個有 getData 方法的 StarWarService 來呼叫 StarWar API 來檢索人員。結果是一個人的 Observable 或未定義的。
import { Component, input } from '@angular/core'; @Component({ selector: 'app-star-war', standalone: true, template: ` <p>Jedi Id: {{ jedi() }}</p> <p>Sith Id: {{ sith() }}</p> `, }) export class StarWarComponent implements OnInit { // required signal input jedi = input.required<number>(); // required signal input sith = input.required<number>(); ngOnInit(): void {} }
絕地武士和西斯都需要訊號輸入;因此,我無法在建構函式中使用它們或透過服務呼叫 toSignal 來初始化欄位。
我實作了 OnInit 介面並存取 ngOnInit 方法中的兩個訊號輸入。
import { Component, VERSION } from '@angular/core'; import { StarWarComponent } from './star-war.component'; @Component({ selector: 'app-root', standalone: true, imports: [StarWarComponent], template: ` <app-star-war [jedi]="1" [sith]="4" /> <app-star-war [jedi]="10" [sith]="44" />`, }) export class App {}
App 元件有兩個 StarWarComponent 實例。 第一個實例的 jedi id 為 1,第二個實例的 id 為 10。實例的 sith id 分別為 4 和 44。
export class StarWarComponent implements OnInit { // required signal input jedi = input.required<number>(); starWarService = inject(StarWarService); injector = inject(Injector); light!: Signal<Person | undefined>; }
在 StarWarComponent 元件中,我注入了 StarWarService 和元件的注入器。此外,我聲明了一個 light Signal 來儲存 toSignal 函數傳回的結果。
interface ToSignalOptions<T> { initialValue?: unknown; requireSync?: boolean; injector?: Injector; manualCleanup?: boolean; rejectErrors?: boolean; equal?: ValueEqualityFn<T>; }
ToSignalOptions 選項有一個注入器屬性。當在註入上下文之外使用 toSignal 函數時,我可以將元件的注入器傳遞給選項。
export class StarWarComponent implements OnInit { // required signal input jedi = input.required<number>(); starWarService = inject(StarWarService); injector = inject(Injector); light!: Signal<Person | undefined>; ngOnInit(): void { this.light = toSignal(this.starWarService.getData(this.jedi()), { injector: this.injector }); } }
在 ngOnInit 方法中,我呼叫服務來取得 Observable,並使用 toSignal 函數建立訊號。第二個參數是組件注入器的選項。
<app-star-war-person [person]="light()" kind="Jedi Fighter" />
接下來,我將光訊號傳遞給 StarWarPersonComponent 組件以顯示絕地武士的詳細資訊。
export class StarWarComponent implements OnInit { // required signal input sith = input.required<number>(); starWarService = inject(StarWarService); injector = inject(Injector); evil!: Signal<Person | undefined>; ngOnInit(): void { // this also works runInInjectionContext(this.injector, () => { this.evil = toSignal(this.starWarService.getData(this.sith())); }) } }
我宣告了一個邪惡的 Signal 來儲存 toSignal 函數傳回的結果。 runInInjectionContext 的第一個參數是元件的注入器。第二個參數是一個回呼函數,它執行 toSignal 函數並將 person 指派給邪惡變數。
<app-star-war-person [person]="evil()" kind="Sith Lord" />
接下來,我將邪惡訊號傳遞給 StarWarPersonComponent 元件以顯示西斯尊主的詳細資料。
如果元件需要訊號輸入,我可以存取 ngOnInit 或 ngOnChanges 中的值來發出 HTTP 請求或其他操作。 然後,我不需要創建效果來觀看所需的信號並調用後端。
鐵人挑戰賽第 33 天到此結束。
參考資料:
以上是將手動注入器傳遞給 toSignal 函數以避免外部上下文注入錯誤的詳細內容。更多資訊請關注PHP中文網其他相關文章!