'Reusing existing Angular Component inside material Dialog
I have an existing component ResultGridComponent that is passed data by @Input(). I want to use the same component for the MatDialog.
Hence I added @Inject(MAT_DIALOG_DATA) data in the constructor of the ResultGridComponent component.
constructor(private spinner: EdpLoaderService, private dialog: MatDialog, @Optional() @Inject(MAT_DIALOG_DATA) data) {}
But when the component is called as per it's regular usage that is not as a dialog, I get the following error :
ERROR Error: StaticInjectorError(AppModule)[ResultGridComponent -> InjectionToken MatDialogData]: StaticInjectorError(Platform: core)[ResultGridComponent -> InjectionToken MatDialogData]: NullInjectorError: No provider for InjectionToken MatDialogData!
I am pretty sure I have imported all the needed modules. Because I created a separate component for the dialog and passed data to it and it worked fine. But I want to reuse the component. And I am pretty sure this error is coming when I call constructor the of the child component as a regular component and the constructor doesn't get the MatDialogData passed to it by the caller.
Solution 1:[1]
In some cases, I would use @Optional() before the @Inject(MAT_DIALOG_DATA).
constructor(
@Optional() @Inject(MAT_DIALOG_DATA) dialogData: any
) {
if (dialogData) {
// here pass dialog data to the child component
}
}
Solution 2:[2]
I was able to find a solution to the situation.
Problem Statement: I was trying to use the existing component (Let's call it ChildComponent) in 2 separate places. One as a regular child component and second, as a component inside Material Dialog.
Challenge: To pass data to the dialog component, we need @Inject(MAT_DIALOG_DATA) in the Child Component. But when you inject the ChildComponent inside the regular component, you don't provide any provider for the '@Inject(MAT_DIALOG_DATA)'. Hence, it fails while initiating. And we do need the '@Inject(MAT_DIALOG_DATA)' if we want to use the ChildComponent inside the Mat Dialog.
Solution: Create a wrapper component, let's call it (ChildWrapperComponent). For the regular use case, directly inject the ChildComponent in the parent component. Whereas for the Mat Dialog use case, provide the ChildWrapperComponent
const dialogRef = this.dialog.open(ChildWrapperComponent, {
width: '90%',
disableClose: false,
data: {
fileName: this.fileName,
results: this.results
}
} );
Now you can have the data inside the ChildWrapperComponent using @Inject(MAT_DIALOG_DATA) data
constructor(private spinner: EdpLoaderService, private dialog: MatDialog,
@Inject(MAT_DIALOG_DATA) data,
private dialogRef: MatDialogRef<ChildWrapperComponent>) {
this.setInputData(data);
}
Once you have the data inside the ChildWrapperComponent, inject the Child Component inside the Wrapper and pass data using @Input().
I am a newbie in Angular. Would highly appreciate better or different solutions.
Solution 3:[3]
I solved it by adding providers like this after reading the dependency-injection-providers
@Component({
...
...
providers: [
{ provide: MatDialogRef, useValue: this },
]
})
After this, my component reads the data from route resolvers if loaded regularly and reads the data from injected MAT_DIALOG_DATA if loaded in dialog in another component.
Solution 4:[4]
Was facing the error
ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(n)[InjectionToken MatDialogData -> InjectionToken MatDialogData -> InjectionToken MatDialogData]:
NullInjectorError: No provider for InjectionToken MatDialogData!
Solved by adding @Optional decorator in front of @Inject(MAT_DIALOG_DATA) in the component constructor
constructor ( @Optional() @Inject(MAT_DIALOG_DATA) public dialogData: any ) { }
If using MatDialogRef add @Optional() in front of MatDialogRef<SomeComponent> too.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Stan |
| Solution 2 | Alexis |
| Solution 3 | Kishor Pawar |
| Solution 4 |
