0%

angular-module

Angular Module

Angular是模块化的,模块是Angular应用的基本构建块,每个Angular应用都至少有一个模块,即根模块,Angular模块是一个带有@NgModule装饰器的类。

模块是一个逻辑单元,是一个自我完备的功能集合,它可以包含组件、服务、指令、管道等,模块可以导入其他模块,也可以导出组件、指令、管道等供其他模块使用,模块还可以提供Service。

以一个电商网站App为例,可以包含如下模块:登录模块(包含登录,注册,找回密码等功能),购物模块,订单模块,支付模块等等。

Generate Angular module with Angular CLI

使用Angular CLI生成一个新的模块,运行如下命令:

1
ng generate module moduleName // or ng g m moduleName

NgModule的组成

以下是一个典型的入口模块的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

declarations

declarations属性用来声明属于该模块的组件、指令、管道等,这些组件、指令、管道等只能在该模块中使用,其他模块不能使用。

  • declarations只能包含component, directive,pipe.
  • component, directive,pipe - 统称为declarable, 每个declarable只能隶属于一个NgModule,如果在多个NgModule中导入同一个declarable,那么会出现如下错误:
1
xxx is declared in multiple Angular modules: AppModule and AppContentModule

imports

imports属性用来导入其他模块,一经导入,那么被导入的模块中包含的组件、指令、管道等可以在当前模块中使用。比如当前模块依赖另一个模块A,那么要把模块A导入到当前模块中,这样当前模块才可以使用模块A中的组件、指令、管道等。

  • imports只能包含module, 或者standalone component.

providers

providers属性用来声明该模块中的服务,这些服务可以在该模块中使用,也可以在该模块导入的其他模块中使用。(我在现实中看见有人把Component放到providers中也能work,这不是好的编程习惯。)

exports

exports属性用来声明该模块中导出的组件、指令、管道等,这些导出的组件、指令、管道等可以在该模块导入的其他模块中使用。注意导出的组件、指令、管道等必须同时在declarations属性中声明。
也就是说exportsdeclarations的子集。

Angular应用的主模块app.module.ts中的exports属性是空的,因为根模块中的组件、指令、管道等都是可以在其他模块中使用的,所以不需要导出。

bootstrap

bootstrap属性用来声明该模块的根组件,根组件是Angular应用的入口组件,一个Angular应用只能有一个根组件,根组件只能在根模块中声明。bootstrap中声明的组件会插入到index.html中的<app-root></app-root>标签中。

注意:bootstrap中可以指定多个组件,这些组件会被插入到index.html中,index.html中根据组件的选择器加在不同的组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--index.html-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>LearningAngular</title>
</head>
<body>
<app-header></app-header>
<app-content></app-content>
<app-footer></app-footer>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

import { AppHeaderComponent } from './app-header/app-header.component';
import { AppContentComponent } from './app-content/app-content.component';
import { AppFooterComponent } from './app-footer/app-footer.component';

@NgModule({
declarations: [
AppHeaderComponent,
AppContentComponent,
AppFooterComponent,
],
imports: [
BrowserModule,
HttpClientModule,
],
providers: [],
bootstrap: [AppHeaderComponent, AppContentComponent, AppFooterComponent],
})
export class AppModule {}

注意: 只有根模块才需要声明根组件,其他模块不需要声明根组件。所以除了app.module.ts之外,其他的module都不需要声明bootstrap属性。

component and module

在Angular standalone component出现以前,任何一个component都必须隶属于一个module。但是在Angular standalone component出现以后,component就可以不用隶属于一个module了,这种component称为standalone component,也就是独立的component。

详情请看Angular Standalone Component

How Angular module identifies components?

Angular module是如何识别Component的呢?对于非standalone component,Angular module是通过declaration来标记的。对于standalone component,Angular module是通过imports来标记的。

非独立组件还分为两种情况,有对应的angular module或者没有对应的angular module

  • angular module - 当前module需要导入对应的angular module,才能使用导入module中的component。(待使用的component需在其module中exports数组中导出)
  • 没有angular module - 则需要将component声明在当前module的declarations中。

Example

假设我们需要定义如下两个组件, 这两个组件都有对应的module

  • ProductHomeModule包含ProductHomeComponent - 产品首页, 该组件内部又调用了ProductDetailComponent
  • ProductDetailModule包含ProductDetailComponent - 产品详情页

首先,需要在ProductDetailModule中声明ProductDetailComponent,并导出ProductDetailComponent

1
2
3
4
5
6
7
8
9
10
11
// product-detail.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductDetailComponent } from './product-detail.component';

@NgModule({
declarations: [ProductDetailComponent], // <-- declare ProductDetailComponent
imports: [CommonModule],
exports: [ProductDetailComponent], // <-- export ProductDetailComponent
})
export class ProductDetailModule {}

然后,需要在ProductHomeModule中导入ProductDetailModule,这样ProductHomeComponent就可以使用ProductDetailComponent了。

1
2
3
4
5
6
7
8
9
10
11
12
// product-home.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductHomeComponent } from './product-home.component';
import { ProductDetailModule } from '../product-detail/product-detail.module';

@NgModule({
declarations: [ProductHomeComponent],
imports: [CommonModule, ProductDetailModule], // <-- import ProductDetailModule
exports: [ProductHomeComponent],
})
export class ProductHomeModule {}

最后,在ProductHomeComponent模板中使用ProductDetailComponent

1
2
<!-- product-home.component.html --> 
<app-product-detail /> <!-- use ProductDetailComponent -->

References: