Arter
Arter

Reputation: 2324

Close dialog component from effect

I need to close angular material dialog from @ngrx/effect

here is my code

import { MatDialogRef, MatDialog } from "@angular/material/dialog";
import { AddComponent } from "./../../add/add.component";

@Injectable()
export class UserEffects {
@Effect()
addNewUser$ = this.actions$.pipe(
    ofType(actions.UserActionTypes.addNewUser),
         mergeMap((user: actions.addNewUser) =>
             this.userService.createUser(user.user).pipe(
                map(() => {
                  new actions.LoadUsers(),
                  this.notificationService.success("User added successfully!");
                  this.dialogRef.close();     <------ // here i try to close
                }),
              catchError(error => of(new actions.Error(error.error)))
            )
        )
    );

constructor(
    private actions$: Actions<actions.UserActions>,
    private userService: UserService,
    private notificationService: NotificationPopUpServiceService,
    public dialogRef: MatDialogRef<AddComponent>
) {}
}

And with this i get error

main.ts:13 NullInjectorError: R3InjectorError[EffectsRootModule -> InjectionToken ngrx/effects: Root Effects -> UserEffects -> MatDialogRef -> MatDialogRef -> MatDialogRef]: NullInjectorError: No provider for MatDialogRef!

What is the best way to close material-dialog from effect or from service? Because we always need to set a name for the dialog component?

Thank you

Upvotes: 8

Views: 4144

Answers (5)

Arter
Arter

Reputation: 2324

I think I find a solution, but If there something better pls, let me know... I add this.dialogRef.closeAll()

class UserEffects {
  constructor(
    private actions$: Actions,
    private dialogRef: MatDialog,
    private notificationService: NotificationService,
  ) {}

  @Effect()
      addNewUser$ = this.actions$.pipe(
      ofType(actions.UserActionTypes.addNewUser),
          mergeMap((user: actions.addNewUser) =>
            this.userService.createUser(user.user).pipe(
              map(() => {
                new actions.LoadUsers(),
                this.notificationService.success("User added successfully!");
                this.dialogRef.closeAll();    <--- //this is the key
              }),
      catchError(error => of(new actions.Error(error.error)))
    )
  ));
}

EDIT:

modal is closed, but I get error

core.js:6014 ERROR Error: Effect "UserEffects.addNewUser$" dispatched an invalid action: undefined

TypeError: Actions must be objects

Any help? Thnx

Upvotes: 2

john invictus
john invictus

Reputation: 69

I think the best solution to closing dialog is subscribing to the effect variable ie

// close the dialog
// Inside the dialog component
 this.userEffects.addNewUser$.pipe(
     ofType(Actions.LoadUsers)
 ).subscribe(_ => this.matDialogRef.close());

Upvotes: 4

Sometimes Hero
Sometimes Hero

Reputation: 21

You could listen to actions$: Actions in your dialog component, subscribe to it and close the dialog when the action is triggered, without recurring to NgRx effects and needing to inject all the dialogs references.

Your dialog component's constructor would include, among other things:

constructor(    
    public dialogRef: MatDialogRef<YourDialogComponent>,
    private readonly actions$: Actions) {}

In ngOnInit you'd listen to the relevant action meant to close the dialog. I generally rely on ngOnDestroy to unsubscribe from observables.

private readonly subscription = new Subscription();

ngOnInit(): void {
    this.subscription.add(
      this.actions$
        .pipe(ofType(actions.UserActionTypes.LoadUsers))
        .subscribe(() => this.dialogRef.close())
    );
}

ngOnDestroy(): void {
    this.subscription.unsubscribe();
}

Pay attention to the action you're listening to; it must be one triggered by the addNewUser$ effect, which seems to be new actions.LoadUsers() in your case. The common pattern is to follow an addNewUser action with an addNewUserSuccess or addNewUserFailure.

You may have several error dialogs popping up at the same time. This approach allows one to manage concurrent dialogs made of different components: closeAll() will (unsurprisingly) close all the material dialogs.

Upvotes: 2

rattkin
rattkin

Reputation: 46

regarding your 'dispatched an invalid action: undefined'

every effect must dispatch an action, unless you specify: { dispatch: false }

Upvotes: 1

Wandrille
Wandrille

Reputation: 6821

In the constructor of your @Effect, you need to provide the dependency:

 private dialogRef: MatDialogRef<MyDialogComponentToClose>

And you need to import MatDialogModule inside your module where your effect is.

Upvotes: 0

Related Questions