Modules imported in AppModule aren't visible in different modules

Viewed 1187

Let's say that this is my AppModule:

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    MaterialModule,
    HomeModule
  ],
  exports: [
    MaterialModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

As you can see I declared MaterialModule over the HomeModule, then I exported that MaterialModule. But still in HomeModule I cannot use components from MaterialModule, error:

1. If 'mat-icon' is an Angular component, then verify that it is part of this module.
2. If 'mat-icon' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. 

So I'm forced to import MaterialModule again in HomeModule. Is it correct behavior or I'm doing something wrong?


I changed Imports in AppModule as @maryrio7 suggested and now is MaterialModule.forRoot(), and I added static method to MaterialModule, still doesn't work.

@NgModule({
    imports: [
        MatButtonModule,
        MatCardModule,
        MatIconModule,
    ],
    exports: [
        MatButtonModule,
        MatCardModule,
        MatIconModule
    ]
})
export class MaterialModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule: MaterialModule,
            providers: []
        };
    }
}
3 Answers

Umm... A common misconception in Angular is that importing a module in a parent module (i.e. in your example, AppModule) creates a hierarchy of modules. Hence, people assume AppModule's child modules (i.e. HomeModule) should also inherit parent module's imported modules.

However, that is not the case. Modules do not inherit access to modules declared in parent modules. Imagine a module as a box. A box will only contain whatever you put inside of it.

Now if you have 3 boxes, box A, box M and box H. If you didn't put box M inside the box H, why would box H contain stuff that belongs to box M?

Similarly, if you have put box M and box H inside of box A. Now, box A would certainly contain all the stuff in both box M and box H, wouldn't it?

Now, switch the term "box" to module, and A to AppModule, M to MaterialModule and H to HomeModule. Does it make more sense now?

To further highlight this in Angular applications, that is why when you generate a new module, a CommonModule is automatically imported in the new module. CommonModule is a "lesser" version of BrowserModule, containing NgIf, NgFor, etc directives that are commonly used in components. Because in the new module, BrowserModule is NOT automatically inherited into it - ngIf, ngFor, etc wouldn't work. You can test this out, by removing CommonModule and see if you can use ngFor, ngIf, etc.


You can easily fix this by creating a SharedModule to import all of your third-party lib's modules. I.e. importing MaterialModule and then exporting it out.

All your (lazily loaded) feature modules will then have to import one single SharedModule instead of having repeating all the imports for 3rd party modules.

E.g. SharedModule

@NgModule({
   imports:[
       CommonModule, FormsModule, ReactiveFormsModule, MaterialModule, 
       //... {other modules that will be shared between modules} ...,
   ],
    exports:[
        CommonModule, FormsModule, ReactiveFormsModule, MaterialModule,
        //... {other modules that will be shared between modules} ...,
    ]
})

It's better to use a SharedModule and then import it wherever you want to use one of his module:

Creating shared modules allows you to organize and streamline your code. You can put commonly used directives, pipes, and components into one module and then import just that module wherever you need it in other parts of your app.

@NgModule({
    imports:      [ MaterialModule],
    exports:      [ MaterialModule ]
})
export class SharedModule { }

Now you can inject the SharedModule everywhere you want, here in HomeModule:

@NgModule({
    imports:      [ SharedModule ] // <-- now you can access MaterialModule
})
export class HomeModule { }

I think if you want to skip the import step, you have to add the forRoot() to the module as follows:

imports: [
    .....
    MaterialModule.forRoot(),
    ....
  ],

EDIT: Try to add this in your MaterialModule.ts

@NgModule({
  exports: [MaterialModule]
})
export class MaterialModule { }
Related