Как сделать несколько HTTP-вызовов в Angular6, используя forkJoin и ngrx?

Сценарий

В моем приложении angular6 у меня есть три категории: catA, catB, catC. Каждой категории нужны данные из 3 API. При нажатии любой категории загружается компонент CategoryDetailsComponent, я отправляю действие (LoadCategoryDetails).

Я реализовал forjoin для выполнения параллельных вызовов API (в сервисе), а также resolver для обеспечения выполнения всех вызовов, только после этого компонент должен загружаться.

Проблема

  • При нажатии catA (или перезагрузке страницы) массив categoryNames пуст
  • При нажатии catB (вторая маршрутизация) массив CategoryNames показывает список предыдущей категории (catA)

Возможно, один из вызовов api требует времени. и мой преобразователь настроен неправильно.

Вопрос

Как настроить эффекты и преобразователь ??

Как использовать forkjoin для выполнения нескольких вызовов и отправки данных компоненту?

Код

  1. dipatch action and subscribe to selector

    ngOnInit() {
        const id = parseInt(this.route.snapshot.paramMap.get('categoryId'), 10);
        this.store.dispatch(new fromCategory.LoadCategoryDetails(id));
    
        this.store.select(getCategoryDetails)
                  .subscribe((data) => {
                    this.categoryDetails = data[0];
                    this.journeys = data[1];
                    this.categories = data[2];
                  });
    
        // filter only category name
        // problem
        this.categories.forEach( category => {
          this.categoryNames.push(category.name);
        });
    }

  2. effects (look for action and call service)

    @Effect()
    loadCategoryDetails$ = this.actions$.pipe(
        ofType<fromCategory.LoadCategoryDetails>(CategoryActionTypes.LoadCategoryDetails),
        switchMap((action) => {
        return this.categoryService.getCategoryDetails(action.payload).pipe(
            map(data => new fromCategory.LoadCategoryDetailsSuccess(data))
        );
        })
    );

  3. service func making multiple calls and using forkjoin

    import { forkJoin } from 'rxjs';
    
    getCategoryDetails(categoryId) {
        const categoryDetails = this.http.get(url);
        const journeys = this.http.get(url);
        const courses = this.http.get(url);
    
        return forkJoin([categoryDetails, journeys, courses]);
    }

  4. resolver

    export class CategoryDetailsResolver implements Resolve<any> {
    
        constructor(
            private categoryService: CategoryService,
            private router: Router) {}
    
        resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
            const categoryId = route.paramMap.get('categoryId');
            return this.categoryService.getCategoryDetails(categoryId);
        }
    }

Заранее спасибо.


person Raj    schedule 26.09.2018    source источник
comment
Зачем нужен резолвер? Вы в основном вызываете один и тот же HTTP-запрос дважды getCategoryDetails (), один раз в преобразователе и еще раз при отправке действия.   -  person JBeckton    schedule 27.09.2018
comment
Еще один вопрос, который стоит задать себе: каков объем состояния сведений о категории? это только для этого одного компонента или состояние необходимо для многих компонентов или модулей? Я вижу, что многие разработчики чрезмерно используют redux, когда это даже неуместно.   -  person JBeckton    schedule 27.09.2018


Ответы (2)


проведите рефакторинг вашего кода, чтобы НЕ использовать route.snapshot, и посмотрите, исправит ли это его.

ngOnInit() {
this.route.data.subscribe((data) => {

if(data) {
 this.store.dispatch(new fromCategory.LoadCategoryDetails(data.id));

 this.store.select(getCategoryDetails)
          .subscribe((data) => {
            this.categoryDetails = data[0];
            this.journeys = data[1];
            this.categories = data[2];

 this.categories.forEach( category => {
  this.categoryNames.push(category.name);
  });
});

}

});

}
person JBeckton    schedule 27.09.2018
comment
Спасибо за ваш ответ. Ваше решение сработало, и я получаю данные в реальном времени. Действительно цените ваше время и усилия, чтобы предоставить решение. Это решение subscribing resolver in the component directly and getting data from service, но хранилище / эффекты здесь не используются. Не могли бы вы рассказать, как я могу получить действие по отправке данных, подписавшись на селектор хранилища, а не напрямую из службы / преобразователя ?? - person Raj; 27.09.2018

Мне нравится ваша реализация forkjoin. Но одна проблема, которую я вижу в вашем коде, заключается в том, что вы повторяете категории за пределами обратного вызова getCategoryDetails. Поскольку это асинхронно, вы всегда должны убедиться, что получаете категории, прежде чем выполнять итерацию. Для этого вы должны переместить код для итерации категорий в функцию успешного обратного вызова.

ngOnInit() {
    const id = parseInt(this.route.snapshot.paramMap.get('categoryId'), 10);
    this.store.dispatch(new fromCategory.LoadCategoryDetails(id));

    this.store.select(getCategoryDetails)
              .subscribe((data) => {
                this.categoryDetails = data[0];
                this.journeys = data[1];
                this.categories = data[2];

this.categories.forEach( category => {
      this.categoryNames.push(category.name);
    });
              });

    // filter only category name
    // problem

}
person Code-EZ    schedule 26.09.2018
comment
Спасибо за ответ. Я пробовал это, и это дает ошибку Cannot read property 'forEach' of undefined. Не могли бы вы проверить правильность effects? Я считаю, что если преобразователь настроен правильно, эта проблема может быть решена. - person Raj; 26.09.2018
comment
@Raj проверьте console.log (данные [2]) и убедитесь, что вы получаете данные правильно. Данные, которые вы получаете, могут быть неопределенными - person Code-EZ; 26.09.2018
comment
да, данные поступают, но изначально они не определены. снимок экрана - person Raj; 26.09.2018
comment
@Raj Вы можете заключить то же самое в условие if (data [2]) {// выполнить итерацию} - person Code-EZ; 26.09.2018
comment
@Raj Я надеюсь, что вы вошли в систему успешного обратного вызова, я думаю, что данные [2] of undefined поступают куда-то еще - person Code-EZ; 26.09.2018
comment
Я также использовал условие if(data[2]){ //do the iteration}, но поскольку api не разрешены, старые данные все еще присутствуют, и я получаю старый массив. снимок экрана - person Raj; 26.09.2018
comment
а также сделал categoryNames = [] в конструкторе, чтобы сбросить значение и загрузить новые данные API. не сработало. - person Raj; 26.09.2018
comment
@Raj Я не думаю, что это из-за вашего кода на стороне клиента, просто проверьте почтальона или скрипача, вы получаете данные, которые вы ожидали - person Code-EZ; 26.09.2018
comment
да, я проверил inpostman, там я получаю правильные данные. В браузере я тоже исправляюсь, но с задержкой. При изменении маршрута массив categoryDetails содержит данные от предыдущего маршрутизатора, которые попадают в categoryNames. - person Raj; 26.09.2018
comment
@Raj вот ваша проблема: const id = parseInt (this.route.snapshot.paramMap.get ('categoryId'), 10); избегайте использования моментального снимка маршрутизатора в повторно используемых компонентах, например / app / my-component /: id, поскольку OnInit не срабатывает должным образом. вместо этого используйте thie.route.data.subscribe () - person JBeckton; 27.09.2018