angular ngModule 中的 entryComponents 是什么?

What is entryComponents in angular ngModule?

我正在开发一个依赖于 angular 2Ionic 应用程序 ( 2.0.0-rc0 )。所以新引入的ngModules被收录了。我在下面添加我的 app.module.ts.

import { NgModule } from '@angular/core';
import { IonicApp, IonicModule } from 'ionic-angular';
import { MyApp } from './app.component';
import { Users } from '../pages/users/users';

@NgModule({
  declarations: [
    MyApp,
    Users
  ],
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    Users
  ]
})
export class AppModule {}

entryComponents 在这里做什么? Components 已在 declarations 中定义。那么重复它们有什么必要呢?如果我不在此处包含组件会怎样?

这适用于使用 ViewContainerRef.createComponent() 添加的动态添加的组件。将它们添加到 entryComponents 告诉离线模板编译器编译它们并为它们创建工厂。

在路由配置中注册的组件也会自动添加到 entryComponents,因为 router-outlet 也使用 ViewContainerRef.createComponent() 将路由组件添加到 DOM。

离线模板编译器(OTC)只构建实际使用的组件。如果组件不直接在模板中使用,OTC 无法知道它们是否需要编译。使用 entryComponents,您可以告诉 OTC 也编译这些组件,以便它们在运行时可用。

What is an entry component? (angular.io)

NgModule docs (angular.io)

Defines the components that should be compiled as well when this component is defined. For each components listed here, Angular will create a ComponentFactory and store it in the ComponentFactoryResolver.

如果您没有向 entryComponents 列出动态添加的组件,您将收到一条关于缺少工厂的错误消息,因为 Angular 不会创建一个。

另见 https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

没有比 Angular 文档更好的解释了:entry-components and ngmodule-faq

下面是 angular 文档中的解释。

An entry component is any component that Angular loads imperatively by type.

A component loaded declaratively via its selector is not an entry component.

Most application components are loaded declaratively. Angular uses the component's selector to locate the element in the template. It then creates the HTML representation of the component and inserts it into the DOM at the selected element. These aren't entry components.

A few components are only loaded dynamically and are never referenced in a component template.

The bootstrapped root AppComponent is an entry component. True, its selector matches an element tag in index.html. But index.html isn't a component template and the AppComponent selector doesn't match an element in any component template.

Angular loads AppComponent dynamically because it's either listed by type in @NgModule.bootstrap or boostrapped imperatively with the module's ngDoBootstrap method.

Components in route definitions are also entry components. A route definition refers to a component by its type. The router ignores a routed component's selector (if it even has one) and loads the component dynamically into a RouterOutlet.

The compiler can't discover these entry components by looking for them in other component templates. You must tell it about them by adding them to the entryComponents list.

Angular automatically adds the following types of components to the module's entryComponents:

  • The component in the @NgModule.bootstrap list.
  • Components referenced in router configuration.

You don't have to mention these components explicitly, although doing so is harmless.

其他答案提到了这一点,但基本的总结是:

  • html 中未使用组件时需要它,例如:<my-component />
  • 例如,您 间接 使用 Angular Material 对话框,它们是在 TS 代码 中创建的,而不是 html:
openDialog() {
    const dialogRef = this.dialog.open(MyExampleDialog, { width: '250px'});
}

这需要您将其注册为 entryComponent:

  • entryComponents: [MyExampleDialog]

否则会报错:

  • ERROR Error: No component factory found for MyExampleDialog. Did you add it to @NgModule.entryComponents?

entryComponents 数组用于仅定义在html 中找不到且动态创建的组件。 Angular 需要这个提示来找到入口组件并编译它们。

入口组件主要有两种类型:

  • 引导的根组件。
  • 您在路由定义中指定的组件。

关于入口组件的更多详细信息,请参阅angular.io https://angular.io/guide/entry-components

一些关于 entryComponent

的背景知识

entryComponent 是命令式加载的任何组件 Angular。您可以通过在 NgModule 或路由定义中引导它来声明 entryComponent

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent] // bootstrapped entry component
})

Documentation下面说

To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.

现在回答你关于 entryComponents

的具体问题

@NgModule 文件中有 entryComponents 个数组。如果使用 ViewContainerRef.createComponent() 引导组件,您可以使用它来添加 entryComponents

也就是说,您是在动态创建组件,而不是通过引导或在模板中创建组件。

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(myComp.component);
const viewContainerRef = this.compHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);

从 Angular 开始,9 entryComponents 不再需要,因为 Ivy 允许弃用此功能,因此可以从模块声明中删除。

Deprecated APIs and features - entryComponents and ANALYZE_FOR_ENTRY_COMPONENTS no longer required

Previously, the entryComponents array in the NgModule definition was used to tell the compiler which components would be created and inserted dynamically. With Ivy, this isn't a requirement anymore and the entryComponents array can be removed from existing module declarations. The same applies to the ANALYZE_FOR_ENTRY_COMPONENTS injection token.

Angular Ivy

Ivy is the code name for Angular's next-generation compilation and rendering pipeline. With the version 9 release of Angular, the new compiler and runtime instructions are used by default instead of the older compiler and runtime, known as View Engine.

在 Angular 13.1 中,不推荐使用 EntryComponent。参考:https://github.com/angular/angular/blob/master/CHANGELOG.md#1310-2021-12-09

另请参阅:https://angular.io/guide/deprecations#entrycomponents-and-analyze_for_entry_components-no-longer-required