本节内容主要是:
- 理清设计思路
- 创建代码结构
- 整理知识点
我们先来看一下最终实现的效果
以上三张图即为文件上传组件的最终实现效果。
从这三张图中可以分析出来,我们需要:
- 文件上传组件(图1 中的上传按钮)
- 模态对话框组件(图2,图3的模态对话框)
下面,我们就开始编写这些组件。
由于我们的目标是做一个可以被重复使用的组件,所以,我们应该先创建一个module,然后在module内开发component。
执行下列命令创建一个module
ng generate module upload
执行下列命令创建文件上传component
ng generate component upload
执行下列命令创建模态对话框component
ng generate component upload/dialog
执行下列命令创建upload service,用于提供和服务器进行交互的接口
ng generate service upload
打开刚才新建的upload module模块,引入依赖的模块,修改如下:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UploadComponent } from './upload.component';
import { MatButtonModule, MatDialogModule, MatListModule, MatProgressBarModule } from '@angular/material';
import { DialogComponent } from './dialog/dialog.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { UploadService } from './upload.service';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [CommonModule,
BrowserAnimationsModule,
HttpClientModule,
MatButtonModule,
MatDialogModule,
MatListModule,
MatProgressBarModule],
declarations: [UploadComponent, DialogComponent],
exports: [UploadComponent],
entryComponents: [DialogComponent],
providers: [UploadService]
})
export class UploadModule {}
上面代码的重点是@NgModule这个装饰器。装饰器实际就是一个方法,它可以用来为宿主提供一些元数据。
对于装饰器的详细用法以及实现原理,这里有一篇博文,有兴趣的可以去看一下 Decorators & metadata reflection in TypeScript: From Novice to Expert (Part I)
太过于原理的东西就不在这里详细描述了,我自己也并没有完全弄懂,我们来仔细分析一下NgModule里面的内容。
首先,看到imports这个数组,官方对于这个数组的解释是这样的:
在imports数组中导入的模块,就可以被本模块“作用域”内的其他组件使用了,“作用域”是我的理解,可能不是非常正确,但是差不多就是这个意思。为了让本模块内的组件能够使用Angular Material提供的功能,我们在imports里引入了 Material*Module。
declarations数组,官方对于这个数组的解释是这样的:
解释的非常清楚,declarations数组中用来声明属于本模块的组件。在declarations数组中声明的组件,就能使用在imports数组中引入的模块。因此,我们的UploadComponent和DialogComponent就可以使用在imports数组中引入的 Material*Module 了。
exports数组,官方对于这个数组的解释是这样的:
exports数组用于指定外部模块导入本模块时可以使用本模块的哪些组件。在这里,我们导出了UploadComponent,那么,当app module的NgModule的imports中引入了upload module以后,在AppComponent中,我们就能直接使用upload component组件了。
entryComponents数组,官方解释:
这个数组涉及的知识点较多,我们需要慢慢分析。
首先,了解一下什么是entry components。先贴一篇官方的文档 Entry Components。
从官方文档中,我们了解到,Angular中的component分为两种,声明式(declarative)和命令式(imperatively)的。声明式的component就是直接在template中显示声明使用的,而命令式的component则是使用代码动态创建的,比如route配置里面所引用的component,并且,命令式的component就是entry component。
为了更好的理解entry component,这里有一篇官方的文档 What is Entry Component。在这篇文章中我们可以了解到,Angular在性能优化方面做了很多的工作,而entry component这个概念扮演了十分重要的角色。配合tree shaker,Angular可以只加载那些真正在运行时用到的组件而忽略那些没有被使用的组件,即便这些组件被声明在了NgModule装饰器的declarations数组中。
如果想要更深入的理解entry component,我们还需要了解一下angular的动态加载机制,可以通过这个官方教程 Dynamic Component Loader 深入理解。
通过上面的分析,很轻易的就可以知道,我们的dialogComponent需要声明在entryComponents数组中。因为我们需要在运行时动态的创建它,当然,也可能完全不创建它。
provider数组,官方解释:
provider数组定义了可用于当前模块范围内的依赖项注入项。在使用provider时,需要注意,provider是有作用域的。这里有两篇官方文档 Providers in Angular, Hierarchical Dependency Injectors。简单来说,angular的依赖注入是单例的,但是不同作用域的依赖注入器会产生不同的实例,详细内容请参照前面介绍的两篇官方文档,解释的非常清楚。值得一提的是,当前模块的providers数组中提供的服务,当其他模块引用当前模块时,其他模块也是直接可用的。
本文结束,下篇继续。