Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 material dialog has issues - Did you add it to @NgModule.entryComponents?

Angular 9.0.0 <

Since 9.0.0 with Ivy, the entryComponents property is no longer necessary. See deprecations guide.

Angular 9.0.0 >

You need to add dynamically created components to entryComponents inside your @NgModule

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    DashboardComponent,
    HomeComponent,
    DialogResultExampleDialog        
  ],
  entryComponents: [DialogResultExampleDialog]

Note: In some cases entryComponents under lazy loaded modules will not work, as a workaround put them in your app.module (root)


You need to use entryComponents under @NgModule.

This is for dynamically added components that are added using ViewContainerRef.createComponent(). Adding them to entryComponents tells the offline template compiler to compile them and create factories for them.

The components registered in route configurations are added automatically to entryComponents as well because router-outlet also uses ViewContainerRef.createComponent() to add routed components to the DOM.

So your code will be like

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    DashboardComponent,
    HomeComponent,
    DialogResultExampleDialog        
  ],
  entryComponents: [DialogResultExampleDialog]

This is happening because this is a dynamic component and you didn't add it to entryComponents under @NgModule.

Simply add it there:

@NgModule({
  /* ----------------- */
  entryComponents: [ DialogResultExampleDialog ] // <---- Add it here

Look at how the Angular team talks about entryComponents:

entryComponents?: Array<Type<any>|any[]>

Specifies a list of components that should be compiled when this module is defined. For each component listed here, Angular will create a ComponentFactory and store it in the ComponentFactoryResolver.

Also, this is the list of the methods on @NgModule including entryComponents...

As you can see, all of them are optional (look at the question marks), including entryComponents which accept an array of components:

@NgModule({ 
  providers?: Provider[]
  declarations?: Array<Type<any>|any[]>
  imports?: Array<Type<any>|ModuleWithProviders|any[]>
  exports?: Array<Type<any>|any[]>
  entryComponents?: Array<Type<any>|any[]>
  bootstrap?: Array<Type<any>|any[]>
  schemas?: Array<SchemaMetadata|any[]>
  id?: string
})

If you're trying to use MatDialog inside a service - let's call it 'PopupService' and that service is defined in a module with:

@Injectable({ providedIn: 'root' })

then it may not work. I am using lazy loading, but not sure if that's relevant or not.

You have to:

  • Provide your PopupService directly to the component that opens your dialog - using [ provide: PopupService ]. This allows it to use (with DI) the MatDialog instance in the component. I think the component calling open needs to be in the same module as the dialog component in this instance.
  • Move the dialog component up to your app.module (as some other answers have said)
  • Pass a reference for matDialog when you call your service.

Excuse my jumbled answer, the point being it's the providedIn: 'root' that is breaking things because MatDialog needs to piggy-back off a component.


In case of lazy loading, you just need to import MatDialogModule in lazy loaded module. Then this module will be able to render entry component with its own imported MatDialogModule:

@NgModule({
  imports:[
    MatDialogModule
  ],
  declarations: [
    AppComponent,
    LoginComponent,
    DashboardComponent,
    HomeComponent,
    DialogResultExampleDialog        
  ],
  entryComponents: [DialogResultExampleDialog]

While integrating material dialog is possible, I found that the complexity for such a trivial feature is pretty high. The code gets more complex if you are trying to achieve a non-trivial features.

For that reason, I ended up using PrimeNG Dialog, which I found pretty straightforward to use:

m-dialog.component.html:

<p-dialog header="Title">
  Content
</p-dialog>

m-dialog.component.ts:

@Component({
  selector: 'm-dialog',
  templateUrl: 'm-dialog.component.html',
  styleUrls: ['./m-dialog.component.css']
})
export class MDialogComponent {
  // dialog logic here
}

m-dialog.module.ts:

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { DialogModule } from "primeng/primeng";
import { FormsModule } from "@angular/forms";

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    DialogModule
  ], 
  exports: [
    MDialogComponent,
  ], 
  declarations: [
    MDialogComponent
  ]
})
export class MDialogModule {}

Simply add your dialog into your component's html:

<m-dialog [isVisible]="true"> </m-dialog>

PrimeNG PrimeFaces documentation is easy to follow and very precise.