rc21
rc21

Reputation: 1872

NullInjectorError: No provider for MatDialogRef

I can't inject MatDialogRef as it described in documentation: https://material.angular.io/components/dialog/overview

When i'm trying to do it i'v got error:

ERROR Error: StaticInjectorError[MatDialogRef]: StaticInjectorError[MatDialogRef]: NullInjectorError: No provider for MatDialogRef!

app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';

import {
	MatInputModule,
	MatDialogModule,
	MatProgressSpinnerModule,
	MatButtonModule,
	MatDialog,
	MatDialogRef
} from '@angular/material';

import { ApiModule } from '../api/api.module';
import { RoutingModule } from '../routing/routing.module';

import { RegistrationComponent } from './components/registration.component';
import { LoginComponent } from './components/login.component';

import { AccountService } from './services/account.service';

@NgModule({
	imports: [
		BrowserModule,
		MatInputModule,
		MatDialogModule,
		MatProgressSpinnerModule,
		MatButtonModule,
		FormsModule,
		RoutingModule,
		ApiModule
	],
	declarations: [
		RegistrationComponent,
		LoginComponent
	],
	entryComponents: [
		LoginComponent,
		RegistrationComponent
	],
	providers: [
		AccountService,
		MatDialog,
		MatDialogRef
	]
})
export class AccountModule {}

home.component.ts

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

import { MatDialog } from '@angular/material';
import { RegistrationComponent } from '../account/components/registration.component';

@Component({
    moduleId: module.id.replace('compiled', 'app'),
    templateUrl: 'home.component.html'
})
export class HomeComponent
{
    constructor(private modalService: MatDialog) {}

    public openModal() : void
    {
        let dialog = this.modalService.open(RegistrationComponent, {});
    }
}

registration.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { MatDialogRef } from '@angular/material/dialog';

import { User } from '../../../models/domain/User';
import { ApiUserService } from '../../api/entity-services/user.service';
import { AuthService } from '../../auth/auth.service';
import { AccountService } from '../services/account.service'

@Component({
	selector: 'registration-component',
	templateUrl: 'app/modules/account/templates/registration.component.html'
})
export class RegistrationComponent
{
	public user :User = new User();

	public errorMessage :string;

	public isLoading :boolean;

	constructor
	(
		private userService :ApiUserService,
		private authService :AuthService,
		private accountService :AccountService,
		private router :Router,
		public dialogRef :MatDialogRef<RegistrationComponent>
	)
	{
		this.isLoading = false;
	}

	public onSubmit(e) :void
	{
		e.preventDefault();
		this.isLoading = true;

		this.userService
			.Create(this.user)
			.subscribe(
				user =>
				{
					this.user.id = user.id;
					this.user.login = user.login;


					this.authService
						.Login(this.user)
						.subscribe(
							token =>
							{
								this.accountService.Load()
									.subscribe(
										account =>
										{
											this.user = account;
											this.isLoading = false;
											this.dialogRef.close();

											let redirectRoute = account.activeScopeId
												? `/scope/${account.activeScopeId}`
												: '/scope-list/';

											this.router.navigate([redirectRoute]);
										},
										error => this.errorMessage = <any>error
									);
							},
							error => this.errorMessage = <any>error
						);
				},
				error => this.errorMessage = <any>error
			);
	}
}

Upvotes: 135

Views: 239871

Answers (22)

Carl3300
Carl3300

Reputation: 1

For me, I found it was best to include the close function otherwise some errors could occur.

providers: [
    {
      provide: MatDialogRef,
      useValue: {
          close: (res: any) => {},
          componentInstance: (res: any) => {},
      },
    }
],

Upvotes: 0

Abel Chipepe
Abel Chipepe

Reputation: 462

I had two situations where I encounter this issue:

  1. When I was using Standalone Application/Components. I was able to use MatDialog but MatDialogRef was an issue. So I ended up using the original CDK DialogModule. And it worked perfectly.

  2. I was using path alias in Angular 13. All the components that were being imported using path alias could not work with MatDialog or MatDialogRef. Using normal import solved the issue.

In summary, avoid importing MatDialog components with path alias, and in standalone applications, use CDK DialogModule, Dialog, DialogRef and everything will be ok. The configurations are mostly the same.

Upvotes: 0

in app.module.ts add in providers MatDialogRef and MAT_DIALOG_DATA and don't forget import in the same module:

import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material/dialog';

...
imports: [MatDialogModule],
providers: [
    { provide: MatDialogRef, useValue: {} },
    { provide: MAT_DIALOG_DATA, useValue: {} },
 ],
...

Upvotes: 1

Alexandre Veillette
Alexandre Veillette

Reputation: 31

Not an answer for the question but might be helpful! if you have the NullInjectorError: No provider for MatDialogRef, MatDialogData error in the test file (.spec.ts) (it was my case), just add

imports: [MatDialogModule],
providers: [
    { provide: MatDialogRef, useValue: {} },
    { provide: MAT_DIALOG_DATA, useValue: {} },
 ],

in the TestBed.configureTestingModule of your component

Upvotes: 3

PierreD
PierreD

Reputation: 963

Don't provide dummy MatDialogRef or you will be unable to use it within the dialog component. For instance if you try to close the modal material will try to call .close() on {} because of the provide: MatDialogRef, useValue: {} and throws.

Take a look at your module (answers above) & imports.

In my case, I unintentionally imported Dialog from cdk instead of MatDialog from material.

import { Dialog } from "@angular/cdk/dialog"; // wrong
...
constructor(private dialog: Dialog) {}
import { MatDialog } from "@angular/material/dialog"; // good
...
constructor(private dialog: MatDialog) {}

Upvotes: 3

AllJs
AllJs

Reputation: 1810

I had this issue a couple of times and in my case, I was trying to implement a modal with optional data passed to it. My solution was to add this piece of code to the parent module that implements the dialog/modal I was trying to display.

I tested this solution in case you implement the providers inside the app.module.ts (Grand-parent, etc.), and it will still work.

// direct.parent.module.ts

// Import statements
import{...} from '';
import{<YOUR_COMPONENT>} from 'YOUR_COMPONENT_PATH';
...

@NgModule({
  declarations: [..],
  imports:[..],
  providers: [ // <== This is the key that made it work
    {
      provide: MatDialogRef,
      useValue: {}
    },
    { provide: MAT_DIALOG_DATA, useValue: {} }, // <== I had to add this too
    <YOUR_COMPONENT> // My Component
  ],
})

export class DirectParentModule { }

Upvotes: 1

GROL COON
GROL COON

Reputation: 53

Adding MatDialogModule to my app.module.ts - solved my problem.

import { MatDialogModule } from '@angular/material/dialog';

Upvotes: 1

Ahmet Arslan
Ahmet Arslan

Reputation: 6150

This error is generally caused by not providing the required modules. Import the outer module to your module where you are using the outer modules component. That will probably solve the problem.

Upvotes: 0

Lucas
Lucas

Reputation: 10313

I had this error when adding dialogs to a service to be shared in many components. Just to clarify, the error wasn't present in the application before moving dialogs to the service. The solution was to include a custom provider MatDialogRef in the main module

  import { DialogService } from './services/dialog.service';
  import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
  ...
  imports: [
    ...
    MatDialogModule
  ],
  providers: [
     {
       provide: MatDialogRef,
       useValue: {}
     },
     DialogService
  ],
  ...

With this provider the service worked as a singleton with my dialogs to be shared and the provider error was gone.

Upvotes: 199

Mohammad Naghsh
Mohammad Naghsh

Reputation: 273

in angular 12 in used "@Optional()" before inject mat dialog like below:

 @Optional() @Inject(MAT_DIALOG_DATA) public data:any

and in main app module added some code like below:

  providers: [
    {
      provide: MatDialogRef,
      useValue: {}
    }
  ],

best Regards.

Upvotes: 5

O-9
O-9

Reputation: 1779

Apparently everything was fine in my code. The problem was that while developing, I had a temporal route for my modal component so I could view it like a any other regular component/view. In the component, I had a button that triggers opening itself in a modal so I can test it how it looks when actually opened.

So I moved the code that opens the Dialog to another component and it seems to solve the issue.

Upvotes: 0

Fuad Javadov
Fuad Javadov

Reputation: 148

the imported paths in the component.ts and in spec.ts must be identical

for example:

// in the component and spec files
import { MatDialogRef } from '@angular/material/dialog';

or

// in the component and spec files
import { MatDialogRef } from '@angular/material';

Upvotes: 3

Javed
Javed

Reputation: 303

Just add @Optional() before your dialogRef declaration.

For example:

constructor(
    @Optional() public dialogRef: MatDialogRef<yourDialogComponentName>
) {
}

Upvotes: 12

Stack Underflow
Stack Underflow

Reputation: 2453

This error can be caused by injecting MatDialogRef in the component calling MatDialog::open rather than in the component that is passed to the open method. The component passed to the MatDialog open method should have MatDialogRef injected in its constructor as done in the example.

@Component({
  selector: 'dialog-overview-example-dialog',
  templateUrl: 'dialog-overview-example-dialog.html',
})
export class DialogOverviewExampleDialog {

  constructor(
    public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
    @Inject(MAT_DIALOG_DATA) public data: DialogData) {}

  onNoClick(): void {
    this.dialogRef.close();
  }

}

Upvotes: 1

Hiep Tran
Hiep Tran

Reputation: 4093

For example you are using app-checkout

It could be happen if you use a component in both 'dialog' and 'normal' way add app-checkout in normal html file.

Solution 1: remove <app-checkout> </app-checkout>if you dont need import it in normal way in html file

Solution 2 using in both dialog and html file. Instead of this:

// ... something have selector: "app-checkout",
 constructor(
    public dialogRef: MatDialogRef<CheckoutComponent>,
    @Inject(MAT_DIALOG_DATA) public dialogData: any
  ) {}

you need to inject it (to make it optional inject):

this way:


  constructor(
    // only need the @Optional() before the public dialogRef 
    @Optional() public dialogRef: MatDialogRef<CheckoutComponent>, 
    @Inject(MAT_DIALOG_DATA) public dialogData: any
  ) {

  }

or this way:

// ... the same above
  private dialogRef = null;
  private dialogData;
  constructor(private injector: Injector) {
      this.dialogRef = this.injector.get(MatDialogRef, null);
      this.dialogData = this.injector.get(MAT_DIALOG_DATA, null);
  }

Upvotes: 29

Indika_Nuwan95
Indika_Nuwan95

Reputation: 679

I had this Error and fixed it what you have to do is

  1. check if you inserted import statement in the typescript file like this

    import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";

  2. check the constructor

      constructor(@Optional() public dialogRef: MatDialogRef<GadgetAuthMessagesComponent>, @Inject(MAT_DIALOG_DATA) public message: string){ }
    
  3. now go to module .ts and check the imports

    import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material/dialog';
    
  4. in module.ts we need to put MatDialogModule name under imports array

    MatDialogModule

  5. now that's done finally we have to update the providers array

      providers:
    [{
      provide: 'gadget-mlt',
      useValue: GadgetMltComponent,
  },
        {provide:MatDialogRef , useValue:{} },

        { provide: MAT_DIALOG_DATA, useValue: {} }

  ],

Now It should work , Hope this Helps someone !

Upvotes: 8

TheZerg
TheZerg

Reputation: 329

Additionally to all of the above, You may also get this error if you have references to the Component selector in any of the template files in your app. MatDialog Components should only be invoked by the MatDialog.dialog.open() method and not through component selector

Unfortunately, the same error message may be thrown for many reasons.

Upvotes: 1

Giolla
Giolla

Reputation: 93

Remove dialogRef from the constructor:

https://stackblitz.com/edit/angular-dialog-update?file=src%2Fapp%2Fapp.component.ts

Upvotes: 1

VIBrunazo
VIBrunazo

Reputation: 1728

For me the problem was that I had added my Dialog component to some other HTML Template just to test it. Once I removed it, the error in OP went away and it just worked.

Upvotes: 3

Charith
Charith

Reputation: 87

  • In your module: importing only MatDialogModule from angular/material is sufficient. Not required to import and provide: MatDialogRef
  • In your code where DialogComponent import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
  • The above are the steps that I have followed. Not sure why you need to import it from the Dialog
  • But, could you please clarify which selector you have used in your html/template? If you have used the "registration-component" in your template, that could be the reason why you would get this error. I tried out the exact same example out of the Material guide. I accidentally used the 'dialog-overview-example-dialog' instead of the 'dialog-overview-example'. = I got the same error you have got.

Upvotes: 4

rc21
rc21

Reputation: 1872

Thanks to the @Edric, i'v solved the problem by importing MatDialogModule, MatDialog and MatDialogRef from @angular/material/dialog instead of @angular/material

Upvotes: 32

Edric
Edric

Reputation: 26821

It could be due to you not including a Web Animations API polyfill which is required for Angular animations to work since it relies on the API.

Here's a caniuse for the browsers that support it natively. Currently only Chrome, Firefox and Opera support the Web Animations API.

Anyway, you have to follow these steps to get a polyfill:

  1. In the terminal, type the following in your console:

    npm install web-animations-js
    
  2. Once you have finished installing, uncomment the line for Angular Animations in polyfills.ts (or add it if it's not there):

    import 'web-animations-js'; // Uncomment this line
    

Alternatively, you can try importing the modules from the separate endpoints, like so:

From:

import { MatButtonModule, MatDialogModule } from '@angular/material';

To:

import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';

Upvotes: 4

Related Questions