3

Quickly

I am trying to dynamically create a ngrx effect.

What I am trying to accomplish:

I want to be able to dispatch actions in my ngrx store and then be able to decide what to do when the action is a success or a failure.

How I am doing it (for now):

I have 3 Actions in my store. LOAD_DOCUMENT, LOAD_DOCUMENT_SUCCESS and LOAD_DOCUMENT_FAILURE. For now, I'm trying to create an Observable from the Actions that listen on LOAD_DOCUMENT_SUCCESS and LOAD_DOCUMENT_FAILURE. When either of them is dispatched, it will call the processPostAction() method. Once the Observable is ready, I call the LOAD_DOCUMENT Action.

Here is my implementation:

@Injectable()
export class DispatchHandlerProcessor<STORE_STATE> {

  actions$: Observable<Action>[] = [];

  constructor(
    private _store: Store<STORE_STATE>,
    private _dispatchedActions: Actions
  ) { } 

  process<SUCC extends Action, FAIL extends Action>(handler: DispatchHandler<SUCC, FAIL>): void {
    const postActionProcessor$: Observable<Action>
      = this._dispatchedActions.pipe(
        ofType(handler.successType, handler.failureType), // Either LOAD_DOCUMENT_SUCCESS or LOAD_DOCUMENT_FAILURE
        tap((action: SUCC | FAIL) => {
          handler.processPostAction(action); // method executed after LOAD_DOCUMENT
      })
    );
    this.actions$.push(postActionProcessor$);
    this._store.dispatch(handler.actionToDispatch); // call LOAD_DOCUMENT
  }

}

The current behaviour:

Currently:

  • the process() method is called.
  • An Observable is created for the effect and added to the array.
  • The LOAD_DOCUMENT Action is called and ends with a LOAD_DOCUMENT_SUCCESS
  • The effect created in the process(...) method in never called
  • So I guess: The effect is not registered (as stated here)

My questions:

How can I dynamically register my effect? Is there a better way to achieve the pattern I am trying to implement?

Small notes: The actions array is going to grow and create memory issues. For now it is just a small hack to get it working. Ideally, I would like to register the effect rather than use this array.

I also realised the @Effect keyword wasn't placed anywhere. I have an error Decorators are not valid here when placing it line 12.

All my effects 'normally' created are running perfectly well.

Remi
  • 51
  • 6

2 Answers2

1

Actually, I resolved it using a subscription to the Actions.

Here is the code:

@Injectable()
export class DispatchHandlerProcessor<STORE_STATE> {

  constructor(
    private _store: Store<STORE_STATE>,
    private _dispatchedActions: Actions
  ) { } 

  process<SUCC extends Action, FAIL extends Action>(handler: DispatchHandler<SUCC, FAIL>): void {
    const subscription: Subscription = this._dispatchedActions.subscribe(action => {
      if(action.type == handler.successType || action.type == handler.failureType) {
        handler.processPostAction(<SUCC | FAIL>action);
        subscription.unsubscribe();
      }
    });
    this._store.dispatch(handler.actionToDispatch);
  }
}

I'm still interested in how to dynamically register @Effect if someone knows the answer though!

Remi
  • 51
  • 6
  • I'm interested in doing something like this, to share one store between multiple angular micro applications in the same page. Have you found a way of registering an effect dynamically ? – manu Oct 06 '20 at 19:47
  • Hi manu, no, I didn't find a way to do so. We ended up using the solution up there. – Remi Nov 12 '20 at 09:42
0

It is possible to register the NgRx Effects service dynamically (not on the routing level) by using the class EffectSources method addEffects. But you must pass an instance of effect service class as an argument, not the class itself.

constructor(
   private readonly _effectSources: EffectSources,
   private readonly _effects: Effects,
) {
   _effectSources.addEffects(effects);
}

And you also need to provide EffectService somehow (providedIn: "root" is probably the easiest option.

@Injectable({ providedIn: "root" })
export class Effects {

}
still_st
  • 363
  • 1
  • 3
  • 7