angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.39k stars 6.76k forks source link

bug(Dialog): Explicit injector does not work #25262

Open adzhiljano opened 2 years ago

adzhiljano commented 2 years ago

Is this a regression?

The previous version in which this bug was not present was

No response

Description

Hey, I recently updated to v14 just for the newly added feature of the Dialog - the explicit injector, but it does not seem to work. I might be doing something wrong or misunderstood the new feature (to provide an Injector without having to provide a ViewContainerRef).

Reproduction

Steps to reproduce: Check out https://stackblitz.com/edit/angular-ozdqwy?file=src%2Fapp%2Fdialog-overview-example.ts (hope this lives long enough) OR

  1. Open the dialog example https://stackblitz.com/run?file=src/app/dialog-overview-example.ts
  2. Add some service @Injectable() export class SomeService { test = 123; }
  3. Create an injector const myInjector = Injector.create({ providers: [{ provide: SomeService }] });
  4. Pass the injector injector: myInjector`` to ``this.dialog.open
  5. Require the service public someService: SomeService in DialogOverviewExampleDialog's constructor

Expected Behavior

SomeService gets provided to DialogOverviewExampleDialog

Actual Behavior

ERROR NullInjectorError: R3InjectorError(AppModule)[SomeService -> SomeService -> SomeService]: 
  NullInjectorError: No provider for SomeService!
    at NullInjector.get (vendor.js:66456:21)
    at R3Injector.get (vendor.js:66652:27)
    at R3Injector.get (vendor.js:66652:27)
    at R3Injector.get (vendor.js:66652:27)
    at NgModuleRef.get (vendor.js:81766:29)
    at ChainedInjector.get (vendor.js:81534:32)
    at lookupTokenUsingModuleInjector (vendor.js:59680:31)
    at getOrCreateInjectable (vendor.js:59732:10)
    at Module.ɵɵdirectiveInject (vendor.js:67961:10)
    at NodeInjectorFactory.DialogOverviewExampleDialog_Factory [as factory] (main.js:153:195)

Environment

Angular CLI: 14.0.5 Angular: 14.0.5 Angular Material: 14.0.4
Node: 16.13.0 Package Manager: npm 8.1.0 OS: win32 x64

Additional info

I glanced into the source code and I think the injector from the config is taken into account only when creating/attaching the container: https://github.com/angular/components/blob/c28bbdeb51c31cbd1f5e06528ef4d21b1650e4a8/src/cdk/dialog/dialog.ts#L224 but not when creating/attaching the actual content component: https://github.com/angular/components/blob/c28bbdeb51c31cbd1f5e06528ef4d21b1650e4a8/src/cdk/dialog/dialog.ts#L311 and it seems that from a DI point of view, the implementation does not tie the content's Injector with the container's Injector. Again, I might be wrong.

Thanks!

BenjaminHutchinson commented 2 years ago

I have experienced the same, I've ended up needing to build components that (in a minor way) duplicate logic in order to specify providers instead of being able to pass up an injector.

mpo-dev commented 2 years ago

I have the same issue, the MatDialog implementation seems to work fine though, but the CDK dialog is unable to resolve the custom dependencies.

adzhiljano commented 2 years ago

Hey @mpo-dev , that is wierd. The example I posted in the Reproduction section uses the MatDialog . AFAIR MatDialog is just a thin wrapper around the cdk's Dialog and this issue should concern both of them.

mpo-dev commented 2 years ago

@adzhiljano In my production code I had the situation where opening a custom component in a dialog with MatDialog would work fine, but opening the same component in a dialog with Dialog would result in one of the custom components dependencies not being resolvable. I need the CDK Dialog because of the custom dialog container.

LeraAl commented 1 year ago

Hello! Have the same issue on our project with MatDialog. Can not divide application to modules, viewContainerRef doesn't work as well. Services, that are injected only to the lazy-loaded module can not be accessible from the Dialog.

LeraAl commented 1 year ago

It seems I found the root cause of the problem.

Component is created with deprecated API. So, it used the deprecated version of createComponent method with componentFactoryResolver. https://github.com/angular/components/blob/main/src/cdk/portal/portal-directives.ts#L155

As a result, component is created with componentFactoryResolver connected to the AppModule if it's not explicitly provided. Is it possible to fix the issue?

yharaskrik commented 1 year ago

I am also receiving this error using MatDialog on Angular 15.2.5

VeselyT commented 9 months ago

We are experiencing the same issue. Instead of providing only injector we are forced to provide also deprecated component factory resolver to instantiate it in the correct dependency injection context

 this.matDialog.open(DialogComp, {
    injector: this.cmpInjector,
    componentFactoryResolver: this.cmpInjector.get(ComponentFactoryResolver),
  })
tonyholt commented 8 months ago

@VeselyT's solution worked for me. I am trying to utilize a service to handle dialogs. Passing the "viewContainerRef" argument wasn't working, but once I used the componentFactoryResolver like VesleyT, it worked. The viewContainerRef option (or the injector option), are not passing the dependencies properly, as noted by others on this issue.

rojasjandro89 commented 7 months ago

Having the same issue. I think the problem is in the ComponentPortal. I'm generating the dialog myself using portals and I'm experiencing the exact same behavior described by others.

Angular Version 17.2.3

mironante commented 5 months ago

We are experiencing the same issue. Instead of providing only injector we are forced to provide also deprecated component factory resolver to instantiate it in the correct dependency injection context

 this.matDialog.open(DialogComp, {
    injector: this.cmpInjector,
    componentFactoryResolver: this.cmpInjector.get(ComponentFactoryResolver),
  })

Thank you! Very helpful!

Abreuvoir commented 3 weeks ago

I'd like to bump this issue, it has caused problems for us with CDK Dialog, Angular & CDK version 18.2, where lazy modules opening dialogs from a providedIn root dialog service did not have access to their lazy services, despite passing their injector. For now the workaround by passing to cdkDialog.open the deprecated ComponentFactoryResolver related to the injector works, but who knows for how long?

injector,
componentFactoryResolver: injector?.get(ComponentFactoryResolver),