Skip to content

MatDialog: When using in dynamically loaded component throws error No provider for MatDialog #19335

Closed

Description

@naveedahmed1

Reproduction

When using MatDialog in a dynamically loaded component it doesn't work and throws error No provider for MatDialog.

Steps to reproduce:

  1. Follow the instructions in this post (https://medium.com/angular-in-depth/lazy-load-components-in-angular-596357ab05d8 ) to create a dynamically loaded component
  2. Add MatDialogModule to the imports array of NGModule for the dynamically loaded component.
  3. ng serve and check the error in console.

Expected Behavior

It should work without throwing any error.

Actual Behavior

Throws below error:

zone-evergreen.js:659 Unhandled Promise rejection: R3InjectorError(AppBrowserModule)[MatDialog -> MatDialog -> MatDialog]: 
  NullInjectorError: No provider for MatDialog! ; Zone: <root> ; Task: Promise.then ; Value: NullInjectorError: R3InjectorError(AppBrowserModule)[MatDialog -> MatDialog -> MatDialog]: 
  NullInjectorError: No provider for MatDialog!
    at NullInjector.get (http://localhost:4200/vendor.js:34269:27)
    at R3Injector.get (http://localhost:4200/vendor.js:48199:33)
    at R3Injector.get (http://localhost:4200/vendor.js:48199:33)
    at R3Injector.get (http://localhost:4200/vendor.js:48199:33)
    at NgModuleRef$1.get (http://localhost:4200/vendor.js:65751:33)
    at Object.get (http://localhost:4200/vendor.js:63486:35)
    at getOrCreateInjectable (http://localhost:4200/vendor.js:38115:39)
    at Module.ɵɵdirectiveInject (http://localhost:4200/vendor.js:52134:12)
    at NodeInjectorFactory.MyDynamicallyLoadedComponent_Factory [as factory] 

Environment

  • Angular: 10.0.0-next.6 (same behavior in other versions e.g. 9.x)
  • CDK/Material: 9.2.3
  • Browser(s): Chrome
  • Operating System (e.g. Windows, macOS, Ubuntu): Windows

Activity

  1. naveedahmed1 commented on May 16, 2020

    @naveedahmed1
    Author

    To reproduce the error, lazy load below component in AppModule

    import { CommonModule } from '@angular/common';
    import { Component, NgModule } from '@angular/core';
    import { MatButtonModule } from '@angular/material/button';
    import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
    
    @Component({
      templateUrl: './my-lazy.component.html'
    })
    export class MylazyComponent {
    
      public dialogRef: MatDialogRef<any>;
      public dialogSub: any;
    
      constructor(public dialog: MatDialog) {
    
      }
    }
    
    @NgModule({
      imports: [
        CommonModule,
        MatButtonModule,
        MatDialogModule
      ],
      declarations: [MylazyComponent]
    })
    class MyLazyComponentModule {
    }
    
    

    Lazy import the above component in AppModule:

            const { MylazyComponent } = await import('./account/enable-notifications.component');
            const myLazyComponentFactory = this.cfr.resolveComponentFactory(MylazyComponent);
            this.myLazyComponentInstance = this.mylazyContainer.createComponent(myLazyComponentFactory, null, this.injector);
    
    
  2. jelbourn commented on May 29, 2020

    @jelbourn
    Contributor

    @naveedahmed1 any chance you could make a reproduction GitHub repo?

  3. added
    needs: clarificationThe issue does not contain enough information for the team to determine if it is a real bug
    and removed
    needs triageThis issue needs to be triaged by the team
    on May 29, 2020
  4. naveedahmed1 commented on May 29, 2020

    @naveedahmed1
    Author

    Thanks @jelbourn , can I add you to a private repo?

  5. jelbourn commented on May 30, 2020

    @jelbourn
    Contributor

    Sure

  6. naveedahmed1 commented on May 30, 2020

    @naveedahmed1
    Author

    Thank you, I have added you to a private repo, just run ng serve and you will notice the error in the console. In AppComponent, I'm trying to dynamically load the EnableNotificationsComponent. Although the component file contains

    @NgModule({
      imports: [
        CommonModule,
        MatButtonModule,
        MatDialogModule
      ],
      declarations: [
        EnableNotificationsComponent
      ]
    })
    class EnableNotificationsModule { }
    

    its still throwing error Unhandled Promise rejection: R3InjectorError(AppBrowserModule)[MatDialog -> MatDialog -> MatDialog]: NullInjectorError: No provider for MatDialog! ; Zone: <root> ; Task: Promise.then ; Value: NullInjectorError: R3InjectorError(AppBrowserModule)[MatDialog -> MatDialog -> MatDialog]: NullInjectorError: No provider for MatDialog!

  7. added
    needs triageThis issue needs to be triaged by the team
    and removed
    needs: clarificationThe issue does not contain enough information for the team to determine if it is a real bug
    on Jun 15, 2020
  8. faradoxuz commented on Jun 17, 2020

    @faradoxuz

    Same problem

  9. jelbourn commented on Jun 18, 2020

    @jelbourn
    Contributor

    I went to look today, but the invitation expired. The code you linked does look like it should work, though.

  10. naveedahmed1 commented on Jun 18, 2020

    @naveedahmed1
    Author

    I added you again, can you please try it now?

  11. jelbourn commented on Jun 23, 2020

    @jelbourn
    Contributor

    @naveedahmed1 I still get a 404 from the invite

  12. naveedahmed1 commented on Jun 23, 2020

    @naveedahmed1
    Author

    That's strange, I just removed you from collaborators and added again

  13. jelbourn commented on Jun 24, 2020

    @jelbourn
    Contributor

    Seems to have worked that time; I'll try to take a peek tomorrow during my weekly scheduled triage time

  14. naveedahmed1 commented on Jun 24, 2020

    @naveedahmed1
    Author

    Thank you so much @jelbourn :)

  15. tc4rdpp commented on Jul 30, 2020

    @tc4rdpp

    Hello, any news on this?
    I am having a similar issue.
    Thanks.

  16. crisbeto commented on Aug 9, 2020

    @crisbeto
    Member

    I believe this is the same issue as #19616. You can check it out for a better explanation.

  17. jelbourn commented on Aug 13, 2020

    @jelbourn
    Contributor

    @naveedahmed1 can you try what @devversion mentioned in #19616? The main thing is to ensure that you're using the MatDialog instance (to call dialog.open) that's provided/imported from the same NgModule in which the lazy-loaded component is declared.

  18. added
    needs: clarificationThe issue does not contain enough information for the team to determine if it is a real bug
    and removed
    needs triageThis issue needs to be triaged by the team
    on Aug 13, 2020
  19. naveedahmed1 commented on Aug 13, 2020

    @naveedahmed1
    Author

    @jelbourn thank you for your response.

    If you take a look at my comment here #19335 (comment) I am doing exactly the same, but the only difference is I am loading this component dynamically and not through router.

  20. devversion commented on Aug 24, 2020

    @devversion
    Member

    @naveedahmed1 I think the issue here is that you use the ComponentFactoryResolver from the root module and directly create the lazy component. It never goes through the lazy app module though, so the MatDialogModule with its's root providers (i.e. MatDialog service) are not having any effect.

    The right solution would be to either:

    1. Add the MatDialogModule to the root module so that the service is available in your whole application
    2. Retrieve the ComponentFactoryResolver for the lazy module and instantiate the component through that.

    If you go with (2), you might want to use Compiler#compileModuleSync, or directly work with NgModuleFactory. If the module is compiled with ngtsc, then the actual class is considered a factory in Ivy and you could wrap it in a NgModuleFactory, create an instance of it and then bootstrap a component with it. We have an internal example here (using Ivy factory shims though):

    const moduleFactory: NgModuleFactory<any> = moduleFactoryExports[`${module.name}NgFactory`];
    const componentType: Type<any> = moduleExports[componentName];
    return moduleFactory.create(injector)
    .componentFactoryResolver.resolveComponentFactory(componentType);

    More general best-practices are probably in the Angular docs or in various great blog posts.

  21. devversion commented on Aug 24, 2020

    @devversion
    Member

    I've created a StackBlitz that shows the issue: https://stackblitz.com/edit/19335-before?file=src/main.ts. Here is an example with fix as per (2): https://stackblitz.com/edit/19335-with-fix?file=src/main.ts

    The code responsible for loading the actual module with it's providers is the following. Disclaimer: Can still be optimized with parallel loading but here it's sufficient for the example:

    async loadComponent() {
      const lazyModule = await import('./app/lazy.module').then(d => d.LazyModule);
      const moduleFactory = this.compiler.compileModuleSync(lazyModule); 
      const moduleInstance = moduleFactory.create(this.injector);
      const lazyComponent = await import('./app/lazy.component').then(d => d.LazyComponent);
      const factory = moduleInstance.componentFactoryResolver
        .resolveComponentFactory(lazyComponent);
      this.vcr.createComponent(factory, 0, this.injector);
    }

    Closing as this seems to work as expected. Please let us know if this does not work. An issue with an updated StackBlitz would be great. Thanks!

  22. removed
    needs: clarificationThe issue does not contain enough information for the team to determine if it is a real bug
    on Aug 24, 2020
  23. angular-automatic-lock-bot commented on Sep 26, 2020

    @angular-automatic-lock-bot

    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.

  24. locked and limited conversation to collaborators on Sep 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions