Запуск http-сообщения после загрузки всех файлов с помощью mergeMap - PullRequest
0 голосов
/ 06 мая 2020

Я пытаюсь сделать POST запрос после загрузки всех файлов. В моей веб-форме пользователь может добавить много файлов для загрузки. У меня есть два API, которые обрабатывают мой запрос.

Один API для моего fileupload, который сохраняет файл в базе данных и возвращает идентификатор (идентификатор документа), а второй API сохраняет досье. Прежде чем я вызываю savedossier API, я должен добавить все идентификаторы документов в объект досье.

Я загружаю файлы с помощью mergeMap:

const fileupload$ = from(this.attachments).pipe(
  mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
);

fileupload$.subscribe(
  (data) => this.dossier.attachments.push(data),
  (err) => console.error('err: ' + err)
);

const savedossier$ = this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);

savedossier$.subscribe(
  (data) => console.log('Dossier saved!'),
  (err) => console.error('err: ' + err)
);

Моя проблема в том, что второй вызов (на savedossier$) не дожидается загрузки всех файлов (потому что это асинхронный c) . Таким образом, идентификаторы документа не добавляются в мой объект досье.

Я тестировал "уродливое" решение вроде этого:

const fileupload$ = from(this.attachments).pipe(
  mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
);

fileupload$.subscribe(
  (data) => onuploaded(data),
  (err) => console.error('err: ' + err)
);

onuploaded(data) {
  this.dossier.attachments.push(data)
  if (this.dossier.attachments.length === this.attachments.length) {
    savedossier$.subscribe(
      (data) => console.log('Dossier saved!'),
      (err) => console.error('err: ' + err)
    );
  }
}

const savedossier$ = this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);

Это решение с оператором if, сравнивающим длины массивов, работает. Но я думаю, что это не рекомендуемый способ.

Есть ли лучшее решение от rx js?

Ответы [ 2 ]

1 голос
/ 06 мая 2020

Если я правильно понимаю, вам нужно сначала завершить все операции fileupload перед сохранением документа, который должен содержать все id s, возвращаемые загрузка файла операций.

Я также предполагаю, что this.attachments - это какой-то массив, содержащий имена вложений.

Если все это правда, я будет действовать следующим образом.

Сначала я бы создал массив Observables, каждый из которых представляет http-вызов, который вы хотите сделать, чтобы сохранить вложение и получить его id в качестве ответа

const saveAttachments = this.attachments).map(
  file => this.http.post<Attachement>(`${this.addAttUrl}`, file),
);

Затем вы можете выполнить все эти наблюдаемые параллельно, используя forkJoin, например,

forkJoin(saveAttachments)

, который возвращает Observable, который излучает, когда все наблюдаемые, переданные как параметр в массиве saveAttachments, завершены. Испускаемое значение представляет собой массив со всеми возвращенными id s.

Теперь вы можете выполнить последнюю операцию, то есть сохранить досье, объединив savedossier$ наблюдаемое с тем, что было возвращено forkJoin . Окончательный код будет выглядеть так:

const saveAttachments = this.attachments).map(
  file => this.http.post<Attachement>(`${this.addAttUrl}`, file),
);
forkJoin(saveAttachment).pipe(
   concatMap(ids => {
      this.dossier.attachments.push(data);
      return this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);
   })
)

Обратите внимание, что вы должны использовать оператор concatMap, чтобы указать, что вы хотите выполнить наблюдаемое, возвращаемое функцией, переданной в качестве параметра в concatMap после предыдущее наблюдаемое, то есть возвращаемое forkJoin, завершено.

Если вы хотите увидеть некоторые шаблоны того, как использовать Observables для составления http-вызовов в различных случаях использования, это статья может быть интересна .

1 голос
/ 06 мая 2020

Вы можете использовать операторы switchMap и tap :

const fileAndSaveDossier$ = from(this.attachments).pipe(
  mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
  tap(data => this.dossier.attachments.push(data)),
  switchMap(() => this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier)),
);

fileAndSaveDossier$.subscribe(
  (data) => console.log('Dossier saved!'),
  (err) => console.error('err: ' + err)
);

Хорошее видео, объясняющее switchMap(): https://www.youtube.com/watch?v=6lKoLwGlglE

Я также не уверен, почему вы используете mergeMap(). Вы можете просто сделать это следующим образом:

const fileAndSaveDossier$ = this.http.post<Attachement>(
  `${this.addAttUrl}`, this.attachments
).pipe(
  tap(data => this.dossier.attachments.push(data)),
  switchMap(() => this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier)),
);
...