web-dev-qa-db-ja.com

角度でforwardRefは何をしますか?

forwardRef は角度で何をしますか、そしてその使用法は何ですか?

ここに

import {Component, Injectable, forwardRef} from '@angular/core';

export class ClassCL { value; }

@Component({
    selector: 'my-app',
    template: '<h1>{{ text }}</h1>',
    providers: [{provide: ClassCL, useClass: forwardRef(() => ForwardRefS)}]
})
export class AppComponent {
    text;

    constructor( myClass: ClassCL ) {
        this.text = myClass.value;
    }
}

Injectable()
export class ForwardRefS { value = 'forwardRef works!' }
7
unos baghaii

から forwardRefに関するAngularのAPIドキュメント

まだ定義されていない参照を参照できます。

たとえば、forwardRefは、DIの目的で参照する必要があるトークンが宣言されているが、まだ定義されていない場合に使用されます。また、クエリの作成時に使用するトークンがまだ定義されていない場合にも使用されます。

Angular In Depth に良い記事があります。

抜粋は次のとおりです。

forwardRefが機能する理由

これで、forwardRefがどのように機能するかという質問が頭に浮かぶかもしれません。実際には、JavaScriptのクロージャがどのように機能するかと関係があります。クロージャ関数内で変数をキャプチャすると、変数値ではなく、変数参照がキャプチャされます。これを示す小さな例を次に示します。

let a;
function enclose() {
    console.log(a);
}

enclose(); // undefined

a = 5;
enclose(); // 5

変数aは、enclose関数が作成された時点では未定義でしたが、変数参照をキャプチャしたことがわかります。したがって、後で変数が5に更新されたときに、正しい値がログに記録されました。

また、forwardRefは、クラス参照をクロージャに取り込む関数であり、関数が実行される前にクラスが定義されます。 Angularコンパイラは関数 resolveForwardRef を使用して、実行時にトークンまたはプロバイダータイプをアンラップします。

7
Spangen

Angularのドキュメントによると:

まだ定義されていない参照を参照できます。

ForwardRefがどのように機能するかをよりよく理解するには、Javascriptの内部で物事がどのように発生するかを理解する必要があると思います。 forwardRefを使用する必要がある特定のケースの例を示しますが、他の異なるケースが発生する可能性があることを考慮に入れてください。

ご存知かもしれませんが、Javascript関数は実行コンテキストの最上位に引き上げられます。関数はそれ自体がオブジェクトであり、他のオブジェクトも関数から作成できます。関数を使用するとプログラマーはオブジェクトインスタンスを作成できるため、ECMAScript 2015は、JavascriptをJavaなどのクラスベース言語に少し近づけるために、ある種の構文糖衣構文を作成しました。クラスに入る:

class SomeClassName { }

Javascriptコンパイラ(私の場合はBabelを使用しています)にアクセスしてこれを貼り付けると、結果は次のようになります。

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var SomeClassName = function SomeClassName() {
  _classCallCheck(this, SomeClassName);
};

最も興味深い部分は、実際のクラスがバックグラウンドの関数であることに気付くことです。関数と同様に、変数もその実行コンテキスト内で引き上げられます。唯一の違いは、関数を呼び出すことはできますが(ポインターが引き上げられていても参照できるため)、変数は引き上げられ、デフォルト値はundefinedになります。変数には、実行時に割り当ての指定された行で、おそらく未定義以外の値が割り当てられます。例えば:

console.log(name);
var name = 'John Snow';

実際には次のようになります。

var name = undefined;
console.log(name) // which prints undefined
name = 'John Snow';

さて、これをすべて念頭に置いて、Angularに飛び込みましょう。アプリに次のコードがあるとします。

import { Component, Inject, forwardRef, Injectable } from '@angular/core';

@Injectable()
export class Service1Service {
    constructor(private service2Service: Service2Service) {
    }

    getSomeStringValue(): string {
        return this.service2Service.getSomeStringValue();
    }
}

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    constructor(private service1Service: Service1Service) {
        console.log(this.service1Service.getSomeStringValue());
    }
}

export class Service2Service {
    getSomeStringValue(): string {
        return 'Some string value.';
    }
}

そしてもちろん、これらのサービスを提供する必要があります。 AppModuleでそれらを提供しましょう:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent, Service1Service, Service2Service } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [Service1Service, Service2Service],
  bootstrap: [AppComponent]
})
export class AppModule { }

AppModuleのメタデータの重要な行は次のとおりです。

providers: [Service1Service, Service2Service]

このコードを実行すると、次のエラーが発生します。

enter image description here

うーん、おもしろい…ここで何が起こっているの?さて、前の説明に基づいて、Service2Serviceはバックグラウンドで関数になりますが、この関数は変数に割り当てられます。この変数は引き上げられますが、その値は未定義です。これらすべてのため、パラメータを解決できません。

forwardRefと入力します

この問題を解決するために、forwardRefという名前の美しい関数があります。この関数が行うことは、関数をパラメーターとして受け取ることです(この例では、矢印関数を使用しています)。この関数はクラスを返します。 forwardRefは、Service2Serviceが宣言されるまで待機し、その後、渡される矢印関数をトリガーします。これにより、Service2Serviceインスタンスを作成するために必要なクラスが返されます。したがって、app.component.tsコードは次のようになります。

import { Component, Inject, forwardRef, Injectable } from '@angular/core';

@Injectable()
export class Service1Service {
    constructor(@Inject(forwardRef(() => Service2Service)) private service2Service) {
    }

    getSomeStringValue(): string {
        return this.service2Service.getSomeStringValue();
    }
}

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    constructor(private service1Service: Service1Service) {
        console.log(this.service1Service.getSomeStringValue());
    }
}

export class Service2Service {
    getSomeStringValue(): string {
        return 'Some string value.';
    }
}

結論として、私が提供した例に基づいて、forwardRefを使用すると、後でソースコードで定義される型を参照できるため、コードがクラッシュするのを防ぎ、コード内で物事を整理する方法の柔軟性が高まります。

私の答えがお役に立てば幸いです。 :)

9
Armando Perez