Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Providers no longer injected in Components instantiated through MatDialog.open() #25073

Closed
JonWallsten opened this issue Jun 14, 2022 · 9 comments

Comments

@JonWallsten
Copy link

JonWallsten commented Jun 14, 2022

Which @angular/* package(s) are the source of the bug?

Don't known / other

Is this a regression?

Yes

Description

After upgrading from Angular 13.x.x to 14.x.x my services are no longer injected into components opened/instantiated through MatDialog.

MatDialog and TestService is provided in TestComponent.
Opening DialogComponent with this.matDialog.open(DialogComponent) from TestComponenent no longer works.
This scenario is currently used by us to make sure components in dialogs get the correct scope based on which component opens the dialog.

I couldn't find any breaking changes in @angular/angular or @angular/material concerning this.

Working example:
https://stackblitz.com/edit/angular-13-provider-works

Not working example:
https://stackblitz.com/edit/angular-14-provider-bug

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/angular-14-provider-bug

Please provide the exception or error you saw

Error: R3InjectorError(AppModule)[TestService -> TestService -> TestService]:
NullInjectorError: No provider for TestService!

Please provide the environment you discovered this bug in (run ng version)

Not sure how to run this on Stackblitz. Sorry!
Reproduced locally earlier today with latest TypeScript/Node 16.
So should not matter which version.

typescript: ~4.0.2
@angular/animations: 14.0.1
@angular/cdk: 14.0.1
@angular/common: 14.0.1
@angular/compiler: 14.0.1
@angular/core: 14.0.1
@angular/forms: 14.0.1
@angular/material: 14.0.1
@angular/platform-browser: 14.0.1
@angular/platform-browser-dynamic: 14.0.1
@angular/router: 14.0.1
rxjs: 7.5.5
tslib: 2.4.0
zone.js: 0.11.5
@JonWallsten JonWallsten changed the title Providers no longer injected in Components instantiated through MatDialog Providers no longer injected in Components instantiated through MatDialog.open() Jun 14, 2022
@JoostK JoostK transferred this issue from angular/angular Jun 14, 2022
@JoostK
Copy link
Member

JoostK commented Jun 14, 2022

Transferring to components repo because I suspect this is not caused by the FW, but rather a change in the Material components.

@JonWallsten
Copy link
Author

@JoostK: Yeah, I was 50/50 about that. Picked the more general project.

@crisbeto
Copy link
Member

The dialog likely worked like this by accident, we didn't put in any intention on where DI would be resolved from by default. In fb9ff16 the services were rewritten to reuse more code which probably moved around the DI tree as well. You can get the old behavior by passing in the ViewContainerRef from the call site.

  constructor(private matDialog: MatDialog, public testService: TestService, private viewContainerRef: ViewContainerRef) {}

  showDialog() {
    this.matDialog.open(DialogComponent, {viewContainerRef: this.viewContainerRef});
  }

@JonWallsten
Copy link
Author

JonWallsten commented Jun 15, 2022

@crisbeto: Oh, it's a really nice accident then. And it really makes sense for our use case. I have multiple instances of an editor, and each editor should have it's own instance of the services they use. So when a particular editor opens some tool connected to that editor in a dialog it should have access to the same instance of the services to access the correct data.
I'll try your workaround (or intended use?). Thanks!

Edit: If this is as designed someone can close this.

@crisbeto
Copy link
Member

Closing, but it can be reopened if something doesn't work out. I also want to note that you have the option of passing in a custom injector if you want to provide your own tokens.

@JonWallsten
Copy link
Author

JonWallsten commented Jun 15, 2022

@crisbeto: I got this error when I tried to add it to my service:
NullInjectorError: No provider for ViewContainerRef!

In this case I have a service that handles all dialogs. So I would essentially have to pass the ViewContainerRef from each component using a dialog each time? That's unfortunately a lot of places.

@crisbeto
Copy link
Member

Injecting a ViewContainerRef doesn't work in a service since it isn't tied to a specific DOM node. Another approach could be to create your own injector and provide the service there. Something like this:

const injector = Injector.create({parent: someParentInjector, providers: [{provide: TestService, useValue: intendedServiceInstance}]});

dialog.open(DialogComp, {injector});

@JonWallsten
Copy link
Author

JonWallsten commented Jun 15, 2022

Ah, I see. Then the quickest solution is definitely to provide the service with a ViewContainerRef from the component where I provide all the services. Then it only has to be done in one place, and it will always be the correct one.
Something like this:
image
image

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants