Что делает forwardRef в angular?

Что делает forwardRef в angular? и каково его использование?

вот пример:

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 work!' }

2 ответа

Решение

С https://angular.io/api/core/forwardRef

Позволяет ссылаться на ссылки, которые еще не определены.

Например, forwardRef используется, когда токен, на который мы должны ссылаться для целей DI, объявлен, но еще не определен. Он также используется, когда токен, который мы используем при создании запроса, еще не определен.

Есть хорошая запись на https://blog.angularindepth.com/what-is-forwardref-in-angular-and-why-we-need-it-6ecefb417d48

Выписка это:

Почему forwardRef работает?

Теперь у вас может возникнуть вопрос, как работает forwardRef. Это на самом деле связано с тем, как работают замыкания в JavaScript. Когда вы захватываете переменную внутри функции замыкания, она захватывает ссылку на переменную, а не ее значение. Вот небольшой пример, чтобы продемонстрировать, что:

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

enclose(); // undefined

a = 5;
enclose(); // 5

Вы можете видеть, что хотя переменная a была неопределенной в момент создания функции enclose, она захватила ссылку на переменную. Поэтому, когда позже переменная была обновлена ​​до 5, она записала правильное значение.

И forwardRef - это просто функция, которая фиксирует ссылку на класс в замыкании, и класс становится определенным до выполнения функции. Угловой компилятор использует функцию resolForwardRef, чтобы развернуть токен или тип провайдера во время выполнения.

Согласно документации 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. Переменной присваивается значение, возможно, отличное от 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]

Если мы запустим этот код, мы получим следующую ошибку:

Хм, интересно... Что здесь происходит? Ну, на основе объяснения, приведенного ранее, 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 позволяет нам ссылаться на типы, которые будут определены позже в нашем исходном коде, предотвращая сбои нашего кода и обеспечивая большую гибкость в том, как мы организуем вещи в нашем коде.

Я действительно надеюсь, что мой ответ послужит вам хорошо.:)

Другие вопросы по тегам