angulardependency-injectionangular-materialangular-dependency-injection

Angular Material dialog not receiving MAT_DIALOG_DATA when created from canActivate


I'm trying to read a query param on a given route in the canActivate function and if it's not there, show a modal saying that it's not going to work ok.

When I think more about this, because it's only supposed to happen when the app is started for the first time, I think I'll move the logic in a provider with APP_INITIALIZER.

That said, I'm quite frustrated that I haven't been able to inject the MAT_DIALOG_DATA properly and I'd like to:

Before I share a minimal repro, let me walk you through the code I wrote:

bootstrapApplication(App, {
  providers: [
    importProvidersFrom(MatDialogModule),
    provideRouter([
      {
        path: '',
        component: FooComponent,
        canActivate: [
          () => {
            const dialog = inject(MatDialog);
            dialog.open(DialogComponent, {
              data: {
                helloWorld: true,
              },
            });
            return true;
          },
        ],
      },
    ]),
  ],
});

I make sure to import MatDialogModule and then I define a dummy route to have a canActivate. In here, I open up a dialog in which I try to pass some data.

The dialog component is defined as the following:

export class DialogComponent {
  @Inject(MAT_DIALOG_DATA) public dialogData: any;
}

and the view

Data received in the dialog:

<pre>{{ dialogData | json }}</pre>

The issue being that dialogData is always undefined here.


I suspect that from the canActivate, I get a different injector and it's somehow messing up with this idea of launching a dialog with data from here. But it should be as easy as swapping the injector? Or even overriding manually the injector to define the MAT_DIALOG_DATA token myself?

Well I've tried that too, and it doesn't work:

const newInjector = Injector.create({
  providers: [
    {
      provide: MAT_DIALOG_DATA,
      useValue: {
        helloWorld: true,
      },
    },
  ],
  parent: inject(Injector),
});

dialog.open(DialogComponent, {
  data: {
    helloWorld: true,
  },
  injector: newInjector
});

I have also tried passing the ViewContainerRef out of desperation but I did not expect that to work.

What am I missing here? Can anyone explain and give a workaround?

Here's a Stackblitz repro.

EDIT:

I just tried to migrate my code to the APP_INITIALIZER and I've got the exact same issue, so I'm out of options.


Solution

  • Please update the dialog component code, we need to use the injector inside the constructor for dependency injection to work I think!

    import { CommonModule } from '@angular/common';
    import { Component, Inject, OnInit } from '@angular/core';
    import { MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog';
    
    @Component({
      selector: 'app-dialog',
      templateUrl: './dialog.component.html',
      styleUrls: ['./dialog.component.css'],
      standalone: true,
      imports: [CommonModule, MatDialogModule],
    })
    export class DialogComponent {
      public dialogData: any;
    
      constructor(@Inject(MAT_DIALOG_DATA) private data: any) {
        this.dialogData = data;
      }
    }
    

    stackblitz