ホームページ  >  記事  >  ウェブフロントエンド  >  外部コンテキスト挿入エラーを回避するために手動インジェクターを toSignal 関数に渡します

外部コンテキスト挿入エラーを回避するために手動インジェクターを toSignal 関数に渡します

Susan Sarandon
Susan Sarandonオリジナル
2024-09-24 10:30:17321ブラウズ

Pass manual injector to the toSignal function to avoid outside Context Injection error

値が使用できないため、必須の信号入力はコンストラクターまたはフィールド初期化子で使用できません。値にアクセスするための私の解決策は、実際のシグナルの変更を監視し、サーバーに対して HTTP リクエストを作成し、シグナルの値を設定することです。このエフェクトを使用しないことについては多くの議論があり、それを削除するための他の解決策を見つけなければなりません。

必要な信号入力は、ngOnInit および ngOnChanges ライフサイクル メソッドでアクセスできます。ただし、それらは注入コンテキストの外にあるため、toSignal はエラーをスローします。これは 2 つの方法で修正できます:

  • 手動インジェクターを toSignal 関数に渡します
  • runInInjectionContext のコールバック関数で 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());
   });
 }
}

コードの変更は次のとおりです:

  • API を呼び出して Observable を返す StarWarService を作成します
  • StarWarComponent は OnInit インターフェイスを実装します。
  • inject 関数を使用してコンポーネントのインジェクターを注入します
  • ngOnInit で、必要な信号入力を使用して StarWar API を呼び出し、Observable から信号を作成します。エラーを回避するには、手動インジェクターを toSignal 関数に渡します。
  • ngOnInit では、runInInjectionContext 関数がインジェクターのコンテキストで toSignal 関数を呼び出します。

StarWarService の作成

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 {}
}

jedi と Sith は両方とも必須の信号入力です。したがって、コンストラクターでそれらを使用したり、フィールドを初期化するサービスで 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 の 2 つのインスタンスがあります。 最初のインスタンスの jedi ID は 1、2 番目のインスタンスの ID は 10 です。インスタンスのシス ID はそれぞれ 4 と 44 です。

手動インジェクターを toSignal に渡してジェダイ戦闘機に問い合わせます

export class StarWarComponent implements OnInit {
 // required signal input
 jedi = input.required<number>();

 starWarService = inject(StarWarService);
 injector = inject(Injector);
 light!: Signal<Person | undefined>;
}

StarWarComponent コンポーネントに、StarWarService とコンポーネントのインジェクターを挿入します。さらに、toSignal 関数から返された結果を格納するためのライト Signal を宣言します。

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 関数を使用してシグナルを作成します。 2 番目の引数は、コンポーネントのインジェクターのオプションです。

<app-star-war-person [person]="light()" kind="Jedi Fighter" />

次に、光信号を StarWarPersonComponent コンポーネントに渡して、ジェダイ戦闘機の詳細を表示します。

runInInjectionContext はコンポーネントのインジェクターで toSignal を実行します

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()));
   })
 }
}

toSignal 関数から返された結果を保存するために邪悪な Signal を宣言します。 runInInjectionContext の最初の引数はコンポーネントのインジェクターです。 2 番目の引数は、toSignal 関数を実行し、人物を evil 変数に割り当てるコールバック関数です。

<app-star-war-person [person]="evil()" kind="Sith Lord" />

次に、邪悪なシグナルを StarWarPersonComponent コンポーネントに渡して、シス卿の詳細を表示します。

コンポーネントに必要な信号入力がある場合、ngOnInit または ngOnChanges の値にアクセスして、HTTP リクエストやその他の操作を行うことができます。 そうすれば、必要なシグナルを監視してバックエンドを呼び出すためのエフェクトを作成する必要がなくなります。

結論:

  • その時点では値が利用できないため、必要な信号入力をコンストラクターで呼び出すことはできません。
  • 必要な信号入力は、ngOnInit メソッドまたは ngOnChanges メソッドで使用できます。
  • toSignal はインジェクションコンテキストの外で実行されるため、ngOnInit メソッドと ngOnChanges メソッドでエラーをスローします
  • ToSignalOptions のインジェクター オプションに手動インジェクターを渡します
  • runInInjectionContext関数のコールバック関数内でtoSignal関数を呼び出します。

これでアイアンマン チャレンジ 33 日目は終了です。

参考文献:

  • toSignal 公式ドキュメント: https://angular.dev/guide/signals/rxjs-interop#injection-context
  • ToSignalOptions: https://angular.dev/api/core/rxjs-interop/ToSignalOptions#
  • RunInInjectionContext: https://angular.dev/api/core/rxjs-interop/ToSignalOptions#
  • GitHub の問題: https://github.com/angular/angular/issues/50947
  • Stackblitz デモ: https://stackblitz.com/edit/stackblitz-starters-xsitft?file=src%2Fstar-war.component.ts

以上が外部コンテキスト挿入エラーを回避するために手動インジェクターを toSignal 関数に渡しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。