Angular 5 自定义文件上传组件(一)
Angular 5 自定义文件上传组件(二)
Angular 5 自定义文件上传组件(三)本节内容主要是:
- 开发dialog component
- 实现upload service并添加测试数据
- 总结开发过程中的知识点
上一节,我们定义了一个方法,用于打开我们自定义的模态对话框。这一节,我们来实现这个模态对话框。
我们已经在第一节创建了我们的自定义模态对话框组件 dialog component。
选择的文件以列表的形式出现在 dialog 中间,每个文件独占一行。
当点击Upload按钮后,界面变化如下:主要变化为
- Add Files变成不可用。
- 添加了一个进度条。
- 隐藏了Cancel按钮。
- Upload按钮的文本内容变成了Finish。
最后,对话框可以有两种关闭方式:
- 未上传完成前点击Cancel按钮。
- 上传完成以后点击Finish按钮。
UI大致就是这个样子,下面我们来写代码。
按照惯例,先来实现component的代码。
经过分析,我们发现,这个对话框的功能主要有这些
- 点击Add Files按钮,弹出文件选择对话框。
- 未上传完成时点击Cancel按钮关闭对话框。
- 上传过程中,Upload按钮不可用。
- 选择好文件以后,点击Upload按钮上传文件。
- 上传完成以后,点击Finish按钮关闭对话框。
点击Add Files按钮,弹出文件选择对话框
addFiles() {
this.file.nativeElement.click();
}
这里的file是对模板中的input元素的引用的Angular封装,可以通过使用nativeElement来获取到最终的input元素。
我们使用Angular提供的ViewChild装饰器来获取模板中定义的元素。
@ViewChild('file') file: ElementRef;
上面代码的意思是将模板中的带'#file'标识的元素赋值给file这个变量,这个赋值的操作将在AfterViewInit事件完成后执行,也就是说,我们最早可以在NgAfterViewInit处理函数中获得到file这个变量的值。
添加文件后的处理函数
我们需要在选择了文件以后,记录一下当前选择的文件,以便后续操作,因此,我们还需要定义添加文件后的处理函数
onFilesAdded() {
const files: { [key: string]: File } = this.file.nativeElement.files;
for (const key in files) {
if (!isNaN(parseInt(key))) {
this.files.add(files[key]);
}
}
}
this.file.nativeElement.files返回的就是input元素的files属性,它的类型是FileList。
获取到了用户选择的文件以后,我们再循环这个文件集合,将文件对象保存到我们本地的files变量中。
files变量定义如下:
public files: Set<File> = new Set();
关闭模态对话框
closeDialog() {
return this.dialogRef.close();
}
要关闭模态对话框,我们需要获取到当前打开的模态对话框的引用,要做到这一点,需要在构造函数注入MatDialogRef这个东西
constructor(public dialogRef: MatDialogRef<DialogComponent>, public uploadService: UploadService) { }
我们先把uploadService放一边,来聊一聊MatDialogRef这个是什么东西。
官方的文档在这里MatDialogRef,文档中的意思就是说这个MatDialogRef代表的是通过MatDialog service打开的模态对话框的引用。但是,我看到这句话的时候是有点疑问的,它是怎么实现的呢?于是我去翻看了源码。它的实现用到了angular cdk的portal这个概念。每个component在创建的时候必须要绑定一个injector,而在MatDialogComponent创建的时候,会同时创建一个PortalInjector,并将自定义的一些依赖放到这个PortalInjector中去,然后,传给MatDialogComponent。这样一来,MatDialogComponent就可以通过依赖注入的形式,拿到当前创建的窗体对象的引用了。
文件上传功能
先来看下文件上传功能的代码
upload() {
this.uploading = true;
this.progress = this.uploadService.upload(this.files);
const allProgressObservables = [];
// tslint:disable-next-line:forin
for (const key in this.progress) {
allProgressObservables.push(this.progress[key].progress);
}
this.canBeClosed = false;
this.dialogRef.disableClose = true;
this.showCancelButton = false;
forkJoin(allProgressObservables).subscribe(end => {
this.canBeClosed = true;
this.dialogRef.disableClose = false;
this.uploading = false;
});
}
代码中,调用了uploadService.upload方法,下面是这个方法的实现
public upload(files: Set<File>): {[key: string]: Observable<number>} {
const status = {};
files.forEach(file => {
status[file.name] = { progress: Observable.interval(100).take(100) };
});
return status;
}
最终效果如下
上面代码中的知识点涉及到了forkJoin操作符,MatProgressBar这个组件的使用,以及Service最后返回假的Observerable做为测试数据。
至此,一个简单的文件上传组件已经完成了,但是功能还太单调了,后期将加入文件预览等功能,尽情期待。