代码之家  ›  专栏  ›  技术社区  ›  SiddAjmera

将一个可观察的<string>和一个数组<Observable<string>>组合成一个可观察的<string,string[]}>

  •  5
  • SiddAjmera  · 技术社区  · 6 年前

    我有一张类似这样的表格:

    Form UI

    这个 Program Name Description 是反应形式的一部分。以及 Feature Image Video(s) 是文件输入。

    我有两个按钮提交表单,另存为草稿和发布。如果填充了所有字段(包括两个文件输入),则将启用发布。“另存为草稿”始终处于启用状态,并将提交表单。

    let featureImageUrl$: Observable<string>;
    const videoUrls$: Observable<string>[] = [];
    // Check if featureImageToUpload was uploaded
    if (this.featureImageToUpload) {
      featureImageUrl$ = this.fileService.upload(`content/programs/images/${this.utils.generateRandomString()}`, this.featureImageToUpload);
    }
    
    // Check if video(s) was/were uploaded
    if (this.videosToUpload) {
      this.utils.range(this.videosToUpload.length).forEach(fileIndex => {
        // tslint:disable-next-line:max-line-length
        const videoUrl$ = this.fileService.upload(`content/programs/videos/${this.utils.generateRandomString()}`, this.videosToUpload[fileIndex]);
        videoUrls$.push(videoUrl$);
      });
    }
    

    this.fileService.upload 只是一个返回 Observable<string>

    this.utils.range 只是一个基于长度返回数字数组的方法。所以如果长度是4,那么它将返回 [0, 1, 2, 3]

    问题是:

    Observable 我可以订阅它,得到这样一个结构的对象:

    {
      featureImageUrl: 'DOWNLOAD URL FOR THE FEATURE IMAGE' || null // null in case the file wasn't uploaded;
      videos: [
        'DOWNLOAD URL FOR VIDEO 1',
        'DOWNLOAD URL FOR VIDEO 2',
        'DOWNLOAD URL FOR VIDEO 3',
        'DOWNLOAD URL FOR VIDEO 4',
        ...
      ] || null // null in case the video(s) were not uploaded;
    }
    

    到目前为止我尝试过的事情:

    我可以用 forkJoin 这样地:

    forkJoin(...videoUrls$, featureImageUrl$)
      .subscribe(downloadUrls => {
        console.log(downloadUrls);
      });
    

    然后根据视频长度提取视频下载网址 videoUrls$ 然后相应地创建我的对象。但我在寻找一种更干净的方法。

    Observable.create 但对于这个特定的用例,我却无法理解。

    Buggy ,我也试过这个:

    forkJoin(forkJoin(videoUrls$), featureImageUrl$)
      .pipe(
        map(([videoUrls, featureImageUrl]) => ({videoUrls, featureImageUrl}))
      );
    

    如果同时选择了特征图像和视频,那么它的效果非常好。但如果我只选择功能图像而不选择任何视频,订阅永远达不到。

    这里有一个 Sample StackBlitz

    1 回复  |  直到 6 年前
        1
  •  2
  •   Jota.Toledo    6 年前

    补充 Buggy

    目前你的方法有两个问题

    1. 您没有初始化 featureImageUrl$ 默认情况下 Observable 价值观。什么时候? forkJoin 接收非 值作为其参数之一(在本例中 undefined

    2. 如果未选择视频,则传递 空列表 到里面去 叉形连接 . 作为 不知道如何处理一个空的流列表,它永远不会发出一个值。

    例如,解决方案如下:

    let featureImageUrl$: Observable<string> = of(null);
    let videoUrls$: Observable<string[]> = of(null);
    if (this.featureImageToUpload) {
      featureImageUrl$ = this.fileToStream(this.featureImageToUpload);
    }
    
    if (this.videosToUpload) {
      videoUrls$ = forkJoin(Array.from(this.videosToUpload)
        .map(file => this.fileToStream(file)));
    }
    
    forkJoin(videoUrls$, featureImageUrl$)
      .pipe(
        map(([videoUrls, featureImageUrl]) => ({ videoUrls, featureImageUrl }))
      )
      .subscribe(data => {
        console.log({data});
      });
    
    ....
    
    private fileToStream(file: File): Observable<string> {
       return this.fileService
         .upload(`content/programs/videos/${this.utils.generateRandomString()}`, file)
    }
    

    你可以看到它在工作 this blitz