Объедините наблюдаемую <string>и массивв одну наблюдаемую <{string, string []}> - PullRequest
0 голосов
/ 29 сентября 2018

У меня есть форма, которая выглядит примерно так:

Form UI

Program Name и Description являются частью Реактивной формы.И Feature Image и Video(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<string>, по существу упаковывая 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);
  });

А затем извлечь видео скачатьURL, основанные на длине videoUrls$, а затем создайте мой объект соответственно.Но я ищу более чистый способ сделать это.

Я также пытался использовать Observable.create, но не смог обернуть его вокруг этого конкретного случая использования.

С предложениями от Buggy я также попробовал это:

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

Он отлично работает в случае, если выбраны функция изображения и видео.Но если я просто выберу Feature Image, а не выберу ни одного видео, подписка никогда не достигнет.

Вот вам Пример StackBlitz для справки.

1 Ответ

0 голосов
/ 29 сентября 2018

Дополняет подход, предложенный Багги

В настоящее время в вашем подходе есть 2 проблемы

  1. Вы не являетесьинициализация featureImageUrl$ значением по умолчанию Observable.Когда forkJoin получает не наблюдаемое значение в качестве одного из аргументов (в данном случае 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)
}

Вы можете видеть, как это работает в этом блице

...