Не удается получить данные с комбинатаПоследний - Angular - PullRequest
0 голосов
/ 05 марта 2020

Я пытаюсь получить файлы, включенные в журналы поста. Что я здесь не так делаю? Данные не поступают позже в цепочке, когда я пытаюсь pipe результат combineLatest. Весь код используется в службе распознавания данных.

return this.API.getPost(route.params.id).pipe(
  switchMap((response: any) => {
    if (response["logs"]) {
      response["logs"].map(logs => {
        if (logs["files"]) {
          return combineLatest(
            ...logs["files"].map(file=>
              this.API.getFile(file.id)
            )
          ).pipe(
            // Not getting any files from this.API.getFile(file.id)
            map(files =>
              files.map(file => ({
                url: this.sanitizer.bypassSecurityTrustResourceUrl(
                  window.URL.createObjectURL(file)
                )
              }))
            )
          ),
            map(files => {
              logs["files"] = files;
              return response;
            });
        }
      });
    }
    return of(response);
  })
)

Ответы [ 2 ]

2 голосов
/ 06 марта 2020

Ваша проблема

Вы не возвращаете наблюдаемую combineLatest в switchMap.

Внутри блока if в switchMap, вы просто создаете неиспользованный карта наблюдаемых.

Очень упрощенно, вы сейчас делаете это:

switchMap(response =>
  if (response["logs"]) {
    response["logs"].map(logs => of(logs));
  }

  return of(response);
}

Я упростил ваш последний комбайн до of, чтобы продемонстрировать проблему. Когда условие if выполнено, блок создаст новый массив, который затем немедленно игнорируется. Это означает, что независимо от вашего условия if, switchMap всегда будет вызывать of(response). Ваш массив combineLatest не будет работать никогда .

Решение

Вам необходимо вернуть какое-то наблюдаемое из вашего блока if. Если вы думаете о типе данных, который вы создаете, это массив наблюдаемых. Поэтому для этого вам понадобится forkJoin для запуска массива наблюдаемых и возврата одной наблюдаемой, на которую switchMap может переключиться.

return this.API.getPost(this.route.params.id).pipe(
  switchMap((response: any) => {
    if (response["logs"]) {
      return forkJoin(response["logs"].map(logs => {
        if (logs["files"]) {
          return combineLatest(
            ...logs["files"].map(file=>
              this.API.getFile(file.id)
            )
          ).pipe(
            map(files =>
              files.map(file => ({
                url: this.sanitizer.bypassSecurityTrustResourceUrl(
                  window.URL.createObjectURL(file)
                )
              }))
            )
          ),
            // Not sure what this is???
            map(files => {
              logs["files"] = files;
              return response;
            });
        }
      }));
    }
    return of(response);
  })
)

Кроме того, я не уверен, какова цель map это то, что я прокомментировал - в настоящее время он не имеет смысла, и это может даже вызвать проблемы компиляции.

DEMO: https://stackblitz.com/edit/angular-5jgk9z

Это демо упрощенная c абстракция вашей проблемы. «Не работающая» версия создает карту наблюдаемых, которые она не возвращает. «Рабочая» версия переключается на forkJoin и возвращает это. В обоих случаях условие защиты блока if выполняется.

Оптимизация наблюдаемого создания

Я думаю, что создание внутренних наблюдаемых можно упростить и сделать более безопасным.

Кажется немного избыточным заключать массив combineLatest в forkJoin, когда вы можете просто использовать forkJoin напрямую.

И чтобы сделать его более понятным, я бы отделил отображение массива от наблюдаемое создание. Это также поможет вам избежать ошибок, когда вы получите пустой массив, идущий в combineLatest или forkJoin.

// inside the "if" block

// flatten files
const files = response["logs"]
  .filter(log => !!log.files)
  .map(log => log.files)
  .flat();

if (files.length > 0) {
  const observables = files.map(file => this.API.getFile(file.id));
  return forkJoin(observables).pipe(
    map(files => {
      files.map(file => ({
        url: this.sanitizer.bypassSecurityTrustResourceUrl(
          window.URL.createObjectURL(file)
        )
      }))
    })
  );
}

При этом используется функция массива .flat(), которая принимает многомерный массив, такой как :

[
 [1,2,3],
 [4,5,6]
]

и сводит это к следующему:

[ 1,2,3,4,5,6 ]

Если вам нужна поддержка IE и у вас нет polyfill, вы можете использовать альтернативу здесь: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat.

Я также рекомендовал бы создать некоторые интерфейсы и использовать строгую типизацию. Вы скоро заблудитесь, если будете полагаться исключительно на правильное именование переменных (разумеется, мы не даем переменным плохие имена ...)

0 голосов
/ 05 марта 2020

Имейте в виду, что combineLatest не будет излучать начальное значение, пока каждое наблюдаемое не выдаст хотя бы одно значение . Это то же поведение, что и withLatestFrom, и может быть ошибкой, поскольку не будет выходных данных и ошибок, но один (или более) из ваших внутренних наблюдаемых объектов, вероятно, не функционирует должным образом, или подписка задерживается.

Может быть, один из ваших вызовов API:

this.API.getFile(file.id)

возвращает ошибку или никогда не завершается?

Это прямо из документации здесь


ОБНОВЛЕНИЕ:

После упоминания в вашем комментарии о том, что вы используете Rx Js 6, вам нужно знать, что combineLatest ожидает массив в качестве аргумента.

Таким образом, вы должны рассмотреть возможность изменения на:

return combineLatest(
  logs["files"].map(
    file => this.API.getFile(file.id);
  ),
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...