Как вернуть данные с разбивкой на страницы из хранилища ИЛИ новые данные из службы API, используя эффекты ngrx в приложении angular2?

Мой вопрос является продолжением это отличный вопрос и ответ о форме данных с разбивкой на страницы в хранилище избыточности. Я использую ngrx/store в приложении angular 2.

{
  entities: {
    users: {
      1: { id: 1, name: 'Dan' },
      42: { id: 42, name: 'Mary' }
    }
  },
  visibleUsers: {
    ids: [1, 42],
    isFetching: false,
    offset: 0
  }
}

Основываясь на приведенной выше форме, я считаю, что если бы смещение (или страница, сортировка и т. д.) полезной нагрузки входящего запроса изменилось, то видимые пользователи изменились бы, а также пользовательские сущности, вызвав БД. У меня есть некоторые действия и функции редуктора, чтобы справиться с этим, и это работает, как и ожидалось. Если смещение остается прежним и пользователь возвращается на страницу так, как он ее оставил, то пользовательские сущности должны быть возвращены хранилищем, а не БД.

Где я борюсь, так это в том, где разместить эту логику и какие операторы rxjs использовать (все еще изучаю это).

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

@Effect loadUsers$ = this.actions$
     .ofType('LOAD_USERS')
     .switchMap(() => this.userService.query()
         .map((results) => {
             return new LoadUsersSuccessAction(results);
         }))
     .catch(() => Observable.of(new LoadUsersFailureAction()));

Моя лучшая идея примерно такая:

@Effect loadUsers$ = this.actions$
     .ofType('LOAD_USERS')
     .withLatestFrom(this.store.select(state => state.visibleUsers.offset))
     .switchMap(([action, state]) => {
         //something that looks like this??
         //this syntax is wrong and I can't figure out how to access the action payload
         state.offset === payload.offset 
            ? this.store.select(state => state.entities.users) 
            : this.userService.query()
         }
         .map((results) => {
             return new LoadUsersSuccessAction(results);
         }))
     .catch(() => Observable.of(new LoadUsersFailureAction()));

Не уверен, как это сделать. Спасибо вперед.


person trevorc    schedule 06.04.2017    source источник
comment
Не имеет отношения к вашему вопросу, но вы должны поместить свой catch в switchMap. Наблюдаемое, возвращаемое catch, будет завершено, и - за пределами switchMap - это увидит завершение наблюдаемого эффекта, и эффект перестанет работать, если произойдет ошибка.   -  person cartant    schedule 07.04.2017
comment
Это действительно так. Размещенный код имеет неправильный формат. Спасибо за внимание. Узнал об этом на днях.   -  person trevorc    schedule 07.04.2017
comment
Если вы хотите создать виртуальное представление только с некоторыми данными из хранилища, взгляните на селекторы: redux.js.org/docs/recipes/ComputingDerivedData.html   -  person maxime1992    schedule 07.04.2017


Ответы (1)


Я не люблю отвечать на свои вопросы, но мне потребовалось довольно много времени, чтобы найти ответ на этот вопрос. Я не приму этот ответ, так как не уверен, что это лучший способ действовать (все еще изучая входы и выходы). Тем не менее, работает отлично.

Я нашел правильный синтаксис в gist на github. Этот код не соответствует моему примеру, но он ясно демонстрирует «2 варианта условных эффектов ngrx», возвращая хранилище или API, наблюдаемые с использованием какого-то условия.

Надеюсь, это поможет кому-то.

Вот:

  @Effect()
  selectAndLoadStore$: Observable<Action> = this.actions$
    .ofType(storeActions.SELECT_AND_LOAD_STORE)
    .withLatestFrom(this.store.select(ngrx.storeState))
    .map(([action, storeState]) => [action.payload, storeState])
    .switchMap(([storeName, storeState]) => {
      const existsInStore = Boolean(storeState.urlNameMap[storeName]);
      return Observable.if(
        () => existsInStore,
        Observable.of(new storeActions.SetSelectedStore(storeName)),
        this.storeService.getByUrlName(storeName)
          .map(store => new storeActions.LoadSelectedStoreSuccess(store))
      );
    });

  @Effect()
  selectAndLoadStore$: Observable<Action> = this.actions$
    .ofType(storeActions.SELECT_AND_LOAD_STORE)
    .withLatestFrom(this.store.select(ngrx.storeState))
    .map(([action, storeState]) => [action.payload, storeState])
    .switchMap(([storeName, storeState]) => {
      const existsInStore = Boolean(storeState.urlNameMap[storeName]);
      let obs;
      if (existsInStore) {
        obs = Observable.of(new storeActions.SetSelectedStore(storeName));
      } else {
        obs = this.storeService.getByUrlName(storeName)
          .map(store => new storeActions.LoadSelectedStoreSuccess(store));
      }
      return obs;
    });
person trevorc    schedule 07.04.2017
comment
У вас есть репо, я мог бы посмотреть, как все это работает вместе? - person The.Wolfgang.Grimmer; 21.10.2020