Reputation: 775
I have a simple reproduction of what I want to achieve here on stackblitz: https://stackblitz.com/edit/angular-zb8kvg
I have a component (here it's the app.component) which a service is declared on (MyService). I need a new instance of MyService each time the component gets opened, so it seems right to me, that the service is declared on component level and not in the module.
Now I want to open a dialog (MatDialog -> TestComponent) from this component which needs the same service instance.
I get a StaticInjectorError as seen in the console output.
How can I use the same service instance in my dialog as my calling component has?
Upvotes: 11
Views: 8292
Reputation: 14493
I encountered the same problem and found a solution that allows the injection of services into dialog components in the usual way. The MatDialogConfig
passed to the MatDialog.open
method has a property called viewContainerRef
with the following documentation:
Where the attached component should live in Angular's logical component tree. This affects what is available for injection and the change detection order for the component instantiated inside of the dialog. This does not affect where the dialog content will be rendered.
Simply pass the current ViewContainerRef
(injected into your host component) and it will work as intended:
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
providers: [MyService]
})
export class AppComponent {
constructor(public dialog: MatDialog, private viewContainerRef: ViewContainerRef) {}
open() {
this.dialog.open(DialogComponent, { viewContainerRef: this.viewContainerRef });
}
}
@Component({
template: "<h1>DialogComponent</h1>"
})
export class DialogComponent {
constructor(private myService: MyService) { }
}
Upvotes: 28
Reputation: 357
You can pass the parent Injector
(@angular/core/injector) into (MatDialog -> TestComponent)
with the help of MatDialogConfig
param of MatDialog.open
import { Component, Injector } from "@angular/core";
import { MyService } from "./my.service";
import { MatDialog } from "@angular/material";
import { TestComponent } from "./test.component";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
providers: [MyService]
})
export class AppComponent {
constructor(public dialog: MatDialog, private injector: Injector) {}
open() {
this.dialog.open(TestComponent, { data: { injector: this.injector } });
}
}
import { Component, Inject, Injector, OnInit } from "@angular/core";
import { MAT_DIALOG_DATA } from "@angular/material/dialog";
import { MyService } from "./my.service";
@Component({
selector: "app-test",
template: "<h1>TestComponent</h1>"
})
export class TestComponent {
myService: MyService;
constructor(@Inject(MAT_DIALOG_DATA) public data: { injector: Injector }) {
this.myService = this.data.injector.get(MyService);
}
}
Upvotes: 4
Reputation: 1227
That is because the dialog, even though it was requested from your app.component, is not a child of it. You can see this if you inspect the DOM elements of the dialog: the dialog is appended somewhere to the root of the body
, as a sibling to the root element of the angular app.
So the dependency injection is working as intended, it's just that the service's scope does not cover the dialog.
Suggested solution: you can pass data in the MatDialogConfig
param of MatDialog.open
, which has a data field.
That data can be used in multiple ways:
Example: https://material.angular.io/components/dialog/examples (see the TS part of the first example)
Upvotes: 4
Reputation: 226
You didn't provide the service in your component the "provide" property is missing
@Component({
selector: 'app-test',
template: '<h1>TestComponent</h1>',
providers: [MyService] //Missing
})
Upvotes: -2