Отправка внутреннего вызова один раз в приложении Angular - PullRequest
0 голосов
/ 30 апреля 2018

Я использую Angular 5 с NgRX.

У меня есть компонент, который определяет форму для редактирования пользовательских настроек. Когда форма загружается, я хочу установить текущее значение настроек в полях ввода.

В настоящее время я отправляю событие для извлечения этих настроек из серверной части всякий раз, когда загружается компонент.

class SettingsComponent implements OnInit {
  settings$ = this.store.select(fromSettings.getSettings);

  constructor(private store: Store<fromSettings.State>) {
  }

  ngOnInit() {
    this.store.dispatch(new SettingsActionTypes.RetrieveSettings());
  }
}

Эффект, который подхватывает это действие:

@Effect()
findSettings$ = this.actions$
    .ofType(SettingsActionTypes.RetrieveSettings)
    .exhaustMap(() =>
        this.settingsService
            .getSettings() // http call
            .map((settings: Settings) => {
                return new SettingsFound({settings});
            })
            .catch(error => of(new SettingsNotFound()))
    );

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

Я знаю, что могу отправлять действие RetrieveSettings в своем компоненте приложения Angular, но это означает, что бэкэнд вызывается независимо от того, что пользователю нужны его настройки.

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

Итак, как я могу выполнить свой внутренний вызов, когда компонент «Настройки» загружается впервые?

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

У меня было похожее требование, и я смог реализовать его, используя класс обслуживания, который содержал состояние и Observables.

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

Код

import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { of } from "rxjs/observable/of";
import { Observable } from "rxjs/Observable";
import { DropdownOption } from "./dropdown-option";

const httpOptions = {
  headers: new HttpHeaders({
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*"
  })
};

@Injectable()
export class ConfigService {
  regions: Array<DropdownOption>;

  constructor(private httpClient: HttpClient) {}

  getRegions(): Observable<Array<DropdownOption>> {
    if (this.regions) {
      return of(this.regions);
    } else {
      return Observable.create(observer => {
        this.httpClient
          .get<Array<DropdownOption>>("config/regions", httpOptions)
          .subscribe(
            data => {
              this.regions = data;
              observer.next(this.regions);
            },
            err => {
              //Handle error
            },
            () => {
              observer.complete();
            }
          );
      });
    }
  }
}
0 голосов
/ 30 апреля 2018

Использование ответа bygrace заставило меня задуматься о применении того же принципа при инициализации компонента.

ngOnInit() {
    this.settings$
        .do(settings => {
            if (!settings) {
                this.store.dispatch(new SettingsActionTypes.RetrieveSettings());
            }
        })
        .take(1)
        .subscribe()
}

Хотя я все еще чувствую, что должно быть более элегантное решение этой проблемы.

0 голосов
/ 30 апреля 2018

Ваш эффект можно проверить в хранилище ngrx и игнорировать действие, если данные уже получены. Если у вас есть какой-то срок действия данных, вы можете проверить это, а не только существующие данные.

Примерно так:

@Effect()
findSettings$ = this.actions$
    .ofType(SettingsActionTypes.RetrieveSettings)
    .withLatestFrom(store.select(...)) // select your state
    .filter(([action, state]) => state == null) // if not initialized
    .exhaustMap(([action]) => ...); // initialize state

Таким образом, вы продолжаете отправлять действие везде, где требуется состояние. Вы также можете использовать распознаватель маршрутов или что-то подобное, чтобы выполнить диспетчеризацию, а не помещать ее в свой компонент. Мне нравится делать это, когда у меня есть несколько компонентов по одному и тому же родительскому маршруту, которым требуются данные, или когда мне нужно существование нескольких частей состояния, и я не хочу повторять диспетчеризацию во многих компонентах.

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

Проблема в целом

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

В ngrx производители, похоже, отсоединены от потребителя по замыслу. Похоже на сообщение автобуса. Это имеет смысл в свете поддержки нескольких действующих лиц (api, client, ...), влияющих на один и тот же кусок состояния. Если у вас есть только один удаленный субъект (api) в качестве продюсера, то, возможно, имеет смысл просто кэшировать данные в вашем api (shareReplay(1)).

...