The correct approach to display success, error messages through NGRX

I know two solutions to this problem: first you need to save the message in its state, which does not seem good, and secondly, subscribe to the ActionSubjectone I am currently using to display messages.

Is there any other solution for this? Also how to set CSS class in a template, and not in a component?

Here is my example:

 this.actionSubject.subscribe(action => {
      if (action.type === fromActions.LOGIN_SUCCESS) {
        this.message$ = action.payload.message;
        this.messageClass = 'alert alert-success';
      }
      if (action.type === fromActions.LOGIN_FAILURE) {
        this.message$ = action.payload.error.message;
        this.messageClass = 'alert alert-danger';
        this.LoginForm.reset();
      }
    })
Run code

It seems too long, and not DRY, I have to do this in every component where I expect to receive a message.

+4
source share
3 answers

An example from the original docks https://github.com/ngrx/effects/blob/master/docs/intro.md

Create an AuthEffects service that describes the source of the login actions:

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthEffects {
  constructor(
    private http: Http,
    private actions$: Actions
  ) { }

  @Effect() login$ = this.actions$
      // Listen for the 'LOGIN' action
      .ofType('LOGIN')
      // Map the payload into JSON to use as the request body
      .map(action => JSON.stringify(action.payload))
      .switchMap(payload => this.http.post('/auth', payload)
        // If successful, dispatch success action with result
        .map(res => ({ type: 'LOGIN_SUCCESS', payload: res.json() }))
        // If request fails, dispatch failed action
        .catch(() => Observable.of({ type: 'LOGIN_FAILED' }))
      );
}

EffectsModule.run :

import { EffectsModule } from '@ngrx/effects';
import { AuthEffects } from './effects/auth';

@NgModule({
  imports: [
    EffectsModule.run(AuthEffects)
  ]
})
export class AppModule { }

. , (.. , ), EffectsModule.runAfterBootstrap. , runAfterBootstrap .

, : https://toddmotto.com/preloading-ngrx-store-route-guards

+3

, , .

:

// component
public isSuccess = merge(
    this.actions.pipe(filter(x => x.type === 'SUCCESS'),mapTo(true)),
    this.actions.pipe(filter(x => x.type === 'FAILURE'),mapTo(false))
);
public message = merge(
    this.actions.pipe(filter(x => x.type === 'SUCCESS'),map(x => x.payload.message)),
    this.actions.pipe(filter(x => x.type === 'FAILURE'),map(x => x.payload.error.message))
);

// template
<div class="alert"
    [class.alert-success]="isSuccess | async"
    [class.alert-danger]="!(isSuccess | async)">
{{ message | async}}
</div>

:

<div class="alert alert-success"
    *ngIf="(action | async).type === 'SUCCESS'">
    {{ (action | async).payload.message }}
</div>
<div class="alert alert-danger"
    *ngIf="(action | async).type === 'FAILURE'">
    {{ (action | async).payload.error.message }}
</div>

reset, , . , actions.ofType(...) . , rxjs , .

, , .

+1

@Kliment Ru @bygrace , , snackbars (Material) .

message.action.ts

import { Action } from '@ngrx/store';

export const MESSAGE = '[Messages] Show Message';

export class Message implements Action {
    readonly type = MESSAGE;
    constructor(
        public payload: {
            message: string;
            action?: string;
            duration?: number;
            callback?: Function;
        }
    ) { }
}

, snackbar ngrx.

message.effect.ts

import { Injectable } from '@angular/core';

import { Effect, Actions } from '@ngrx/effects';
import * as MessageActions from '../actions/message.action';

import { tap, map } from 'rxjs/operators';
import { MatSnackBar } from '@angular/material';
import { first } from 'rxjs/operators/first';

@Injectable()
export class MessageEffects {

    constructor(
        private actions$: Actions,
        private snackBar: MatSnackBar
    ) { }

    @Effect({ dispatch: false })
    navigate$ = this.actions$
        .ofType(MessageActions.MESSAGE)
        .pipe(
            map((action: MessageActions.Message) => action.payload),
            tap(({ message, action, duration, callback }) => {
                duration = duration ? duration : 3000;

                // incase of an action assigned, subscribe to the snackbar, else just show the message
                if (callback) {
                    this.snackBar.open(message, action, { duration: duration })
                        .onAction()
                        .pipe(
                            first()
                        )
                        .subscribe(() => {
                            callback();
                        });
                } else {
                    this.snackBar.open(message, action, { duration: duration });
                }

            }));

}

, .

, , :

this.store.dispatch(new fromRoot.Message({ message: 'Something went wrong, please try again later' }));

a simple one-line interface that encapsulates all the logic and user interface of messages in your application, the good thing behind it is that I can change my snackbar to whatever I want using any library, and I should change the code only in one place.

0
source

Source: https://habr.com/ru/post/1693801/


All Articles