Angular 5 自定义文件上传组件(四)

Angular 5 自定义文件上传组件(一)
Angular 5 自定义文件上传组件(二)
Angular 5 自定义文件上传组件(三)

本节内容主要是:

  1. 开发dialog component
  2. 实现upload service并添加测试数据
  3. 总结开发过程中的知识点

上一节,我们定义了一个方法,用于打开我们自定义的模态对话框。这一节,我们来实现这个模态对话框。
我们已经在第一节创建了我们的自定义模态对话框组件 dialog component。

先来看一下最终渲染效果
dialog渲染效果

点击 Add Files后出现选择文件对话框
选择文件对话框

选择模式为多选,选择两个文件后,界面变化如下
多选文件界面变化

选择的文件以列表的形式出现在 dialog 中间,每个文件独占一行。

当点击Upload按钮后,界面变化如下:
上传完成后界面

主要变化为

  1. Add Files变成不可用。
  2. 添加了一个进度条。
  3. 隐藏了Cancel按钮。
  4. Upload按钮的文本内容变成了Finish。

最后,对话框可以有两种关闭方式:

  1. 未上传完成前点击Cancel按钮。
  2. 上传完成以后点击Finish按钮。

UI大致就是这个样子,下面我们来写代码。
按照惯例,先来实现component的代码。

经过分析,我们发现,这个对话框的功能主要有这些

  1. 点击Add Files按钮,弹出文件选择对话框。
  2. 未上传完成时点击Cancel按钮关闭对话框。
  3. 上传过程中,Upload按钮不可用。
  4. 选择好文件以后,点击Upload按钮上传文件。
  5. 上传完成以后,点击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 = new Set();
关闭模态对话框
closeDialog() {
  return this.dialogRef.close();
}

要关闭模态对话框,我们需要获取到当前打开的模态对话框的引用,要做到这一点,需要在构造函数注入MatDialogRef这个东西

constructor(public dialogRef: MatDialogRef, 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): {[key: string]: Observable} {
    const status = {};
    files.forEach(file => {
      status[file.name] = { progress: Observable.interval(100).take(100) };
    });

    return status;
  }

最终效果如下


上传文件

上面代码中的知识点涉及到了forkJoin操作符,MatProgressBar这个组件的使用,以及Service最后返回假的Observerable做为测试数据。

至此,一个简单的文件上传组件已经完成了,但是功能还太单调了,后期将加入文件预览等功能,尽情期待。

你可能感兴趣的:(Angular 5 自定义文件上传组件(四))