Условно отправить действие в зависимости от результата предыдущего действия - PullRequest
1 голос
/ 03 августа 2020

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

Если бы это был синхронный код, это было бы что-то вроде :

if (this.backendService.isValid(dataset)) {
    this.backEndService.update(dataset)
}

Определение двух действий (и их соответствующих успешных и ошибочных действий):

export const validateDataset = createAction('[Datasets API] Validate Dataset', props<{ dataset: DataSetDto }>());
export const validateDatasetSuccess = createAction('[Datasets API] Validate Dataset Success', props<{ validationErrors: SgErrorMessage[]}>());
export const validateDatasetFail = createAction('[Datasets API] Validate Dataset Fail', props<{ error: string }>());

export const updateDataset = createAction('[Datasets API] Update Dataset', props<{ dataset: DataSetDto }>());
export const updateDatasetSuccess = createAction('[Datasets API] Update Dataset Success');
export const updateDatasetFail = createAction('[Datasets API] Update Dataset Fail', props<{ errorEntity: SgErrorEntity }>());

Попытка 1

Если у меня есть эффект, который прослушивает updateDataset и пытается сначала отправить validateDataset, он, кажется, никогда не используется, когда применяется .pipe() (и, вероятно, здесь я должен использовать селектор типа hasValidationErrors, возвращающий bool, вместо того, чтобы также использовать оператор filter):

updateDataset$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(DatasetApiActions.updateDataset),
        mergeMap(action => of(DatasetPageActions.validateDataset({dataset: action.dataset})).pipe(
            withLatestFrom(this.store.pipe(select(getValidationErrors))),
            filter(([action, validationErrors]) => validationErrors.length > 0),
            mergeMap(([action, validationErrors]) => this.datasetService.editDataSet(action.dataset).pipe(
                map(() => DatasetApiActions.updateDatasetSuccess()),
                catchError(error => of(DatasetApiActions.updateDatasetFail({error})))
            ))
        ))
    );
});

Попытка 2

Вместо этого я попытался разделить его на действие saveDataset, которое должно вызывать оба действия validateDataset и updateDataset последовательно, а также фильтровать updateDataset запускаться только в том случае, если состояние validationErrors пусто. Я использовал concatMap, чтобы действия могли запускаться последовательно, и они, вероятно, так и есть, но ответы об успехе / неудаче приходят позже, и поэтому данные проверки нельзя использовать:

saveDataset$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(DatasetPageActions.saveDataset),
        concatMap(action => concat(
            of(DatasetPageActions.validateDataset({dataset: action.dataset})),
            of(DatasetApiActions.updateDataset({dataset: action.dataset}))
        ))
    );
});

updateDataset$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(DatasetApiActions.updateDataset),
        withLatestFrom(this.store.pipe(select(getValidationErrors))),
        filter(([action, validationErrors]) => validationErrors.length == 0),
        tap(([action, validationErrors]) => console.log("validationErrors: " + validationErrors))
    );
}, {dispatch: false});

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

С чем я борюсь. заключается в том, что я не могу добавить эффект к действию validateDatasetSuccess для запуска действия updateDataset, потому что он может быть отправлен пользователем отдельно (а не только путем сохранения). Есть ли способ добиться такого поведения?

1 Ответ

2 голосов
/ 04 августа 2020

Вы можете попробовать это:

updateDataset$ = createEffect(() => {
  return this.actions$.pipe(
    ofType(DatasetApiActions.updateDataset),

    // dispatch the `validate` action
    tap(action => this.store.dispatch(DatasetPageActions.validateDataset({dataset: action.dataset}))),

    switchMap(
      action => this.store.pipe(
        select(getValidationErrors),
        
        // we want to skip the current state
        // after all, the store is just a `BehaviorSubject`,
        // so subscribing to it will return the current state
        skip(1),
        filter(validationErrors => /* ... your condition here ... */),
        mapTo(action),
      ),
    ),
    mergeMap(
      action => this.datasetService.editDataSet(action.dataset).pipe(
        map(() => DatasetApiActions.updateDatasetSuccess()),
        catchError(error => of(DatasetApiActions.updateDatasetFail({error})))
      )
    )
  );
});
...