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

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

Susan Sarandon
リリース: 2024-09-24 10:30:17
オリジナル
343 人が閲覧しました

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 サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート