【问题标题】:Observable value not updated on UIUI 上未更新的可观察值
【发布时间】:2021-06-15 04:14:04
【问题描述】:

我正在开发一个 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];
}

attachments.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;
}

files.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并没有从界面中删除。

我还尝试在 file-progress.pipe 中使用pure: false,这样,上传完成后,如果我单击另一个元素,则会反映更改并从中删除进度UI(但据我所知,使用pure: false 会导致性能不佳。

【问题讨论】:

    标签: angular typescript rxjs-observables


    【解决方案1】:

    您需要使用ChangeDetectorRef 服务

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

    constructor(private detector: ChangeDetectorRef)
    

    演示:

     constructor(private detector: ChangeDetectorRef) {
      }
    
    someMethod(){
     setInterval(() => {
    
              this.detector.detectChanges();
    
            }, 5000);
    }
    

    【讨论】:

    • files-state.service 中使用 ChangeDetectorRef 我得到了 NullInjectorError
    • 您不在服务中调用它,而是在您的组件中完成服务方法调用之后
    【解决方案2】:

    我设法通过在成功时发出除了文件大小之外的空值来解决这个问题。

    private onUploadComplete(observer: Subscriber<number>, file: File) {
        return () => {
          observer.next(file.size);
          observer.next(null);
          observer.complete();
    
          this.filesStateService.removeProgress(fileUrl);
        };
    }
    

    【讨论】:

      猜你喜欢
      • 2018-07-27
      • 1970-01-01
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 2016-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多