what does do forwardRef in angular?

uno*_*aii 6 forward-reference angular

What does forwardRef do in angular, and what is its usage?

here is an example:

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!' }
Run Code Online (Sandbox Code Playgroud)

Arm*_*rez 9

根据Angular的文档:

允许引用尚未定义的引用。

我认为,为了更好地了解forwardRef的工作原理,我们需要了解Javascript背后的情况。我将提供一个特定情况的示例,您可能需要使用forwardRef,但要考虑到可能会出现其他情况。

众所周知,Javascript函数位于其执行上下文的顶部。函数是对象本身,其他对象也可以从函数创建。由于函数允许程序员创建对象实例,因此ECMAScript 2015创建了某种语法糖,以使Javascript更加接近基于类的语言(如Java)。输入课程:

class SomeClassName { }
Run Code Online (Sandbox Code Playgroud)

如果我们进入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);
};
Run Code Online (Sandbox Code Playgroud)

最有趣的部分是注意到我们的类实际上是在后台运行的函数。像函数一样,变量也悬挂在其执行上下文中。唯一的区别是,尽管我们可以调用函数(因为即使悬挂了指针也可以引用它的指针),但变量仍被悬挂并赋予默认值undefined。在运行时,在赋值的给定行中,为变量分配了一个值,可能不是未定义的值。例如:

console.log(name);
var name = 'John Snow';
Run Code Online (Sandbox Code Playgroud)

实际上变成:

var name = undefined;
console.log(name) // which prints undefined
name = 'John Snow';
Run Code Online (Sandbox Code Playgroud)

好了,考虑到所有这些,现在让我们进入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.';
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,我们需要提供这些服务。让我们在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 { }
Run Code Online (Sandbox Code Playgroud)

AppModule元数据中的重要一行是:

providers: [Service1Service, Service2Service]
Run Code Online (Sandbox Code Playgroud)

如果运行此代码,将出现以下错误:

在此处输入图片说明

嗯,很有趣...这是怎么回事?好吧,根据之前的解释,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.';
    }
}
Run Code Online (Sandbox Code Playgroud)

总之,基于我提供的示例,forwardRef允许我们引用稍后在源代码中定义的类型,从而防止代码崩溃并在组织代码中的方式方面提供更大的灵活性。

我真的希望我的回答对您有帮助。:)

  • 谢谢你的例子!确实可以帮助我理解`forwardRef`。那么,如果我们在源代码中更早地定义了“ Service2Service”呢?那么我们就不需要使用`forwardRef`了吗?但是就像您说的那样,它为组织代码提供了更大的灵活性。 (2认同)
  • @RyanEfendy 我发现了一些看起来很奇怪但很有意义的东西,请参阅:https://github.com/angular/material2/blob/8050f633b56b6c048fc72dad2ab79304afdad19/src/lib/checkbox/checkbox-required-validator.ts(是的,有时你会组织代码在同一个文件中。这对我来说很有意义,但这是另一个主题。只是想展示一个有意义的例子) (2认同)

Spa*_*gen 7

来自Angular的API文档forwardRef

允许引用尚未定义的引用。

例如,forwardRef当声明需要为DI引用的令牌但尚未定义该令牌时使用。当我们尚未定义创建查询时使用的令牌时,也会使用它。

Angular In Depth有不错的文章 。

这是摘录:

为什么forwardRef起作用?

现在,您的脑海中可能浮现出一个问题forwardRef。实际上,这与JavaScript中的闭包如何工作有关。当您在闭包函数中捕获变量时,它将捕获变量引用,而不是变量值。这是一个小例子,以证明这一点:

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

enclose(); // undefined

a = 5;
enclose(); // 5
Run Code Online (Sandbox Code Playgroud)

您可以看到,尽管在创建函数时a未定义变量enclose,但它捕获了变量引用。因此,稍后将变量更新为时,5它将记录正确的值。

并且forwardRef只是一个函数,它将类引用捕获到闭包中,并且在执行该函数之前定义了类。Angular编译器在运行时使用函数resolveForwardRef解开令牌或提供程序类型。