Search code examples
angularangular17mat-dialog

MatDialog .... NullInjectorError: No provider for DialogService


I want to use MatDialog in Angular 17 as a generic yes/no confirmation dialog. The call for it looks like this:

      selectedAnswer = await this.dlg.ShowConfirmDialog("Abandon Changes?", "Are you sure you want to abandon the changes you have made and load a new account?", DialogTypeEnum.alert);

if (selectedAnswer) {

--Do This

}
else{

  --Do something else
}

I am not getting any errors at compile, but the browser console shows this

NullInjectorError: NullInjectorError: No provider for DialogService!
at NullInjector.get (core.mjs:5682:27)
at R3Injector.get (core.mjs:6125:33)
at R3Injector.get (core.mjs:6125:33)
at R3Injector.get (core.mjs:6125:33)
at R3Injector.get (core.mjs:6125:33)
at R3Injector.get (core.mjs:6125:33)
at ChainedInjector.get (core.mjs:15427:36)
at lookupTokenUsingModuleInjector (core.mjs:4193:39)
at getOrCreateInjectable (core.mjs:4241:12)
at Module.ɵɵdirectiveInject (core.mjs:12045:19)

I can't figure out what I am missing. Any recommendations greatly appreciated. :)

This is my code:

account.components.ts(simplified)

    import { DeclarationListEmitMode } from '@angular/compiler';
import { ChildToParentService } from '../../services/child-to-parent-service';
import { DialogService } from '../../services/dialog.service';
...AND  OTHER IMPORTS



@Component({
  selector: 'app-accounts',
  standalone: true,
  imports: [
    MaterialUiModule,
    CommonModule,... . . . .
  ],

  templateUrl: './accounts.component.html',
  styleUrl: './accounts.component.scss'
})

export class AccountsComponent implements OnInit {

//DECLARATIONS . . . . .


  constructor(private accountsService: AccountsService,
    private childToParentService: ChildToParentService,
    private dlg: DialogService
  ) {

  async loadAccount(accountId: string) {
    var selectedAnswer = true;
    if (this.profile.isDirty()) {
      selectedAnswer = await this.dlg.ShowConfirmDialog("Abandon Changes?", "Are you sure you want to abandon the changes you have made and load a new account?", DialogTypeEnum.alert);
    }
    if (selectedAnswer) {
      this.processes_editProcess.ResetProcessEditForm();
      this.page.isEditMode = false;

 
      this.accountsService.GetAccount(accountId).subscribe((response: any) => {
        this.page.data = response;
      });
    }
  }
  }
}

DialogService.ts

import { MatDialog } from '@angular/material/dialog';
import { Observable, lastValueFrom } from 'rxjs';
import { ConfirmComponent } from '../dialogs/confirm/confirm.component';
import { IConfirmDialog } from '../global/confirm-dialog-Interface';
import { DialogType, DialogTypeEnum, DialogTypes } from 'app/global/dialog-types';
import { Component } from '@angular/core';


@Component({

  templateUrl: '../dialogs/confirm/confirm.component.empty.html',

})

export class DialogService {

  constructor(private dialog: MatDialog) {
    
  }

  async ShowConfirmDialog(title: string, message: string, dialog_Type: DialogTypeEnum) {

    var dt = new DialogTypes();
    var selectedDialogType = new DialogType();

    switch (dialog_Type) {
      case DialogTypeEnum.alert:
        selectedDialogType = dt.alert;
        break;
      case DialogTypeEnum.information:
        selectedDialogType = dt.information;
        break;
      case DialogTypeEnum.success:
        selectedDialogType = dt.success;
        break;
    }

    var audio = new Audio(selectedDialogType.soundSrc);

    audio.load()
    audio.volume = 0.05;
    audio.play();
    const answer = await lastValueFrom(
      this.confirmDialog({
        title: title,
        message: message,
        confirmCaption: 'Yes',
        cancelCaption: 'No',
        icon: selectedDialogType.icon,
        color: selectedDialogType.color,
        soundSrc: selectedDialogType.soundSrc
      }));
    /*     this.dialog.closeDialog(); */
    return answer;
  }

  public confirmDialog(data: IConfirmDialog): Observable<boolean> {
    return this.dialog
      .open(ConfirmComponent, {
        data,
        width: '400px',
        disableClose: true,
      })
      .afterClosed();
  }
  closeDialog() {
    this.dialog.closeAll();
  }

}

Solution

  • AS @Yong Shun said the Dialog Service should be decorated as @Injectable

    You can learn more about @Injectable and Dependency Injection here

    @Injectable({
      providedIn: 'root',
    })
    export class DialogService {
    
      constructor(private dialog: MatDialog) {
        
      }
    
      async ShowConfirmDialog(title: string, message: string, dialog_Type: DialogTypeEnum) {
    
        var dt = new DialogTypes();
        var selectedDialogType = new DialogType();
    
        switch (dialog_Type) {
          case DialogTypeEnum.alert:
            selectedDialogType = dt.alert;
            break;
          case DialogTypeEnum.information:
            selectedDialogType = dt.information;
            break;
          case DialogTypeEnum.success:
            selectedDialogType = dt.success;
            break;
        }
    
        var audio = new Audio(selectedDialogType.soundSrc);
    
        audio.load()
        audio.volume = 0.05;
        audio.play();
        const answer = await lastValueFrom(
          this.confirmDialog({
            title: title,
            message: message,
            confirmCaption: 'Yes',
            cancelCaption: 'No',
            icon: selectedDialogType.icon,
            color: selectedDialogType.color,
            soundSrc: selectedDialogType.soundSrc
          }));
        /*     this.dialog.closeDialog(); */
        return answer;
      }
    
      public confirmDialog(data: IConfirmDialog): Observable<boolean> {
        return this.dialog
          .open(ConfirmComponent, {
            data,
            width: '400px',
            disableClose: true,
          })
          .afterClosed();
      }
      closeDialog() {
        this.dialog.closeAll();
      }
    
    }