首页 > 解决方案 > UI 上未更新的可观察值

问题描述

我正在开发一个 Angular 应用程序和其他功能,现在我正在开发一个图片库(轮播)。

当用户打开图片库时,他还可以上传新照片。

因为我需要了解全局的上传过程,所以我正在尝试为此实现一个服务,如下所示:

ts组件中,我具有以下功能:

onUploadFile(files: File[]) {
    if (!files?.length) {
      return;
    }

    const uploading = this.attachmentsService
      .uploadMultiple(files)
      .map((x) => ({ attachment: x }));

    uploading.forEach((file) => {
      this.filesStateService
        .getProgress(file.attachment.url)
        .pipe(untilDestroyed(this))
        .subscribe({
          complete: () => {
            this.referenceAttachments = [...this.referenceAttachments];
          },
          error: () => {
            this.deleteAttachment(file);
          },
        });
    });

    this.referenceAttachments = [...uploading, ...this.referenceAttachments];
}

附件.service.ts

uploadMultiple(files: File[]) {
    if (!files?.length) {
      return [];
    }

    return files.map((x) => this.upload(x));
}

upload(file: File) {
    const fileUpload = this.filesService.upload(file);

    this.filesStateService.addProgress(fileUpload.url, fileUpload.progress$);

    const attachment: Attachment = {
      url: fileUpload.url,
      size: file.size,
      type: file.type,
    };

    return attachment;
}

文件.service.ts

upload(file: File) {
    ...
    return {
      url: fileUrl,
      progress$: new Observable<number>((observer) => {
        blockBlobClient
          .uploadData(file, {
            onProgress: this.onProgress(observer),
          })
          .then(
            this.onUploadComplete(observer, file, fileUrl),
            this.onUploadError(observer, fileUrl)
          );
      }).pipe(distinctUntilChanged(), startWith(0), shareReplay()),
    };
}

private onUploadError(observer: Subscriber<number>, fileUrl: string) {
    return (error: any) => {
      observer.error(error);

      this.filesStateService.removeProgress(fileUrl);
    };
}

private onUploadComplete(
    observer: Subscriber<number>,
    file: File,
    fileUrl: string
) {
    return () => {
      observer.next(file.size);

      this.filesStateService.removeProgress(fileUrl);

      observer.complete();
    };
}

private onProgress(observer: Subscriber<number>) {
    return (progress: TransferProgressEvent) =>
      observer.next(progress.loadedBytes);
}

在视图中,进度呈现如下:

<app-file-upload-progress
     *ngIf="file.attachment.url | fileProgress | async as progress"
     [progress]="progress"
     [size]="file.attachment?.size"
></app-file-upload-progress>

file-progress.pipe只是从列表中返回进度:

@Pipe({
  name: 'fileProgress',
})
export class FileProgressPipe implements PipeTransform {
  constructor(private filesStateService: FilesStateService) {}

  transform(value: string): Observable<number> {
    if (!value) {
      return null;
    }

    return this.filesStateService.getProgress(value);
  }
}

以及存储文件进度的files-state.service :

@Injectable()
export class FilesStateService {
  filesProgress: Map<string, Observable<number>> = new Map();

  addProgress(url: string, progress$: Observable<number>): void {
    this.filesProgress.set(url, progress$);
  }

  removeProgress(url: string): void {
    this.filesProgress.delete(url);
  }

  getProgress(url: string): Observable<number> {
    return this.filesProgress.get(url);
  }
}

我不明白为什么,在文件成功上传并从filesProgress列表中删除进度后,app-file-upload-progress没有从界面中删除。

我还尝试pure: falsefile-progress.pipe中使用,并且在上传完成后,如果我单击另一个元素,则会反映更改并从 UI 中删除进度(但从我读过的内容来看,使用pure: false将导致表现不佳。

标签: angulartypescriptrxjs-observables

解决方案


您需要使用ChangeDetectorRef 服务

当代码的某些区域(角度)无法使用默认检测检测更改时,ChangeDetector 是必需的。好消息是,在您的代码中使用 ChangeDetector 只需一行代码。您所要做的就是在更改之后this.detector.detectChanges(); 这还需要您之前在构造函数中声明了服务。

constructor(private detector: ChangeDetectorRef)

演示:

 constructor(private detector: ChangeDetectorRef) {
  }

someMethod(){
 setInterval(() => {

          this.detector.detectChanges();

        }, 5000);
}

推荐阅读