【问题标题】:Combine an Observable<string> and an Array<Observable<string>> into a single Observable<{string, string[]}>将 Observable<string> 和 Array<Observable<string>> 组合成一个 Observable<{string, string[]}>
【发布时间】:2019-03-05 07:33:09
【问题描述】:

我有一个看起来像这样的表单:

Program NameDescription 是响应式表单的一部分。 Feature ImageVideo(s) 是文件输入。

我有两个提交表单的按钮,另存为草稿和发布。如果所有字段都已填写,包括文件输入,则发布将被启用。另存为草稿始终处于启用状态,并将提交表单。

提交表单时: 我检查是否有特征图像。如果有,我会将其上传到 Firebase 存储桶。我对视频应用相同的检查。如果有视频,我会一一上传到 Firebase 存储桶。这是相同的代码:

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&lt;string&gt; 的方法,本质上包装了已上传文件的下载 URL。

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$的长度提取视频下载url,然后相应地创建我的Object。但我正在寻找一种更清洁的方式来做到这一点。

我也尝试过使用Observable.create,但对于这个特定的用例无法理解。

根据Buggy的建议,我也尝试了这个:

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

如果同时选择了特色图片和视频,它就可以正常工作。但是,如果我只选择 Feature Image 而不是不选择任何视频,则订阅永远不会到达。

这里有一个Sample StackBlitz 供您参考。

【问题讨论】:

  • forkJoin(forkJoin(videoUrls$), featureImageUrl$) .pipe( map(([videoUrls, featureImageUrl])=&gt;({videoUrls, featureImageUrl})) ) 合适吗?
  • @Buggy,感谢您的评论。让我检查一下。 :)
  • 好的。 So it works fine when both the Feature Image file and the videos are selected and then submitted.但是当未选择其中一个时不起作用。在后一种情况下,它会永远卡住,不过我会用这种方法更新问题。非常感谢。 :)
  • @SiddAjmera 好像featureImageUrl$ 你还没有分配任何值,你可以尝试分配像let featureImageUrl$: Observable&lt;string&gt; = of(''); 这样的初始值,这至少足以完成数据流跨度>

标签: angular rxjs observable rxjs6


【解决方案1】:

补充Buggy建议的方法

您的方法目前存在 2 个问题

  1. 您没有使用默认的 Observable 值初始化 featureImageUrl$。当forkJoin 接收到一个非 observable-like 值作为其参数之一(在本例中为 undefined)时,它将引发异常。

  2. 当没有选择视频时,您将一个空列表传递给内部forkJoin。由于forkJoin 不知道如何处理一个空的流列表,它永远不会发出值。

解决方案例如如下:

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看到它工作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 2021-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    • 1970-01-01
    相关资源
    最近更新 更多