13

I'm building an application using Angular paired with Angular Material, and I'm having some issues with my modules structure.

As the guidelines suggest, importing the MaterialModule is deprecated and should no longer be done, which is why I've made a separate MaterialModule where I only import the Material modules I need to use; this module is then imported in a SharedModule, which is eventually imported everywhere it's needed, including the main AppModule.

One of the Material components I'm using is the MdTooltip, and it all works fine except for when I test my app on a tablet: what happens is that the tooltips don't react to long taps like they're supposed to, and they won't open. The only way I've managed to make it work is by importing the full MaterialModule (from @angular/material) in my AppModule, which is horrendously wrong and inelegant. Any other approach didn't quite seem to cut it, as they would all bring their own problems while not solving the ordeal.

These are my modules (stripped of redundant import statements):

MaterialModule:

import { NgModule } from '@angular/core';
import {...} from '@angular/material';

@NgModule({
  imports: [
    MdGridListModule,
    MdButtonModule,
    MdTabsModule,
    MdToolbarModule,
    MdCardModule,
    MdInputModule,
    MdSelectModule,
    MdAutocompleteModule,
    MdIconModule,
    MdTooltipModule
  ],
  exports: [
    MdGridListModule,
    MdButtonModule,
    MdTabsModule,
    MdToolbarModule,
    MdCardModule,
    MdInputModule,
    MdSelectModule,
    MdAutocompleteModule,
    MdIconModule,
    MdTooltipModule
  ],
  providers: [
    MdIconRegistry
  ]
})

export class MaterialModule {}

SharedModule:

import { MaterialModule } from '../material/app-material.module';
@NgModule({
  imports:      [
    CommonModule,
    MaterialModule,
    FlexLayoutModule,
    FormsModule,
    ReactiveFormsModule
  ],
  declarations: [
    NavbarComponent,
    SearchFiltersComponent,
    RightCurrencyPipe
  ],
  exports:      [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    MaterialModule,
    FlexLayoutModule,
    NavbarComponent,
    RightCurrencyPipe,
    SearchFiltersComponent
  ],
  providers: [
    SpinnerService,
    ProductsService,
    StatePersistenceService
  ]
})

export class SharedModule {}

AppModule:

import { MaterialModule } from "@angular/material";
@NgModule({
  declarations: [
    AppComponent,
    ProductPageComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    SharedModule,
    CoreModule,
    LoginModule,
    MaterialModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Am I doing something wrong? How would you go about dividing your app into submodules?

Thanks in advance

1
  • 2
    i did same thing u did but i get "StaticInjectorError[ElementRef]:" error, everything is loaded but angular material components doesn't work. do u have any idea why? Commented Jan 6, 2018 at 4:54

4 Answers 4

2

Depending on your compiling and deployment strategy you'll want to use tree shaking (for dead-code elimination or live-code import). This is the main motivation for MaterialModule being deprecated. The official suggestion is to only import the components you need in the modules that need it. Your solution of creating a single MaterialModule with all the imports is undoing that aspect but might work depending on your project structure (if you're only using a single module for example).

Try removing MaterialModule from your SharedModule exports. That way you only have a single point of entry for the module in your app root.

Sign up to request clarification or add additional context in comments.

3 Comments

That was an insightful answer, but there's something that I failed to get through with my original question. The components declared and exported by SharedModule make extensive use of the Material components, which makes importing my MaterialModule (or the single components, for that matter) necessary; tooltips, though, don't work properly. What makes it work is importing the deprecated MaterialModule in my AppModule while leaving my custom MaterialModule imported in my SharedModule. Why do I need the full MaterialModule to be imported at the root? This is what bugs me
You might want to check the installed versions in node_modules: the deprecated MaterialModule shouldn't do anything at all with the latest versions. As I can see you have two components in SharedModule and two in AppModule (It's recommend to place these in their own module). What if you didn't export from SharedModule and imported the specific ones in your AppModule that you need for it's components (AppComponent, ProductPageComponent)?
You're right about the deprecated MaterialModule doing basically nothing, only being an empty class. I've tried importing all the single modules I need in each module, and everything is working except the tooltip touch feature. For some reason, the single, only way to make it work is importing the deprecated MaterialModule in my main AppModule, no matter what else I do. If I import it, I get the touch feature, if I don't import it, it doesn't work. This doesn't make much sense to me, to be honest, could it depend on where hammer.js and BrowserModule are imported?
2

Your approach is great. The structure that you have presented is an alternative that was presented in material.angular.io. I am unsure to why your tooltip does not work. But I would like to advise you to only use your custom MaterialModule only once on the root module. There is no need to import it in the SharedModule again.

Comments

1

Your first mistake is services in SharedModule. SharedModule should not have Providers array. CoreModule is used for services.

You don't need to import all stuff in shared module. SharedModule is for exports usually. Also MaterialModule does not need imports because it does not use them. Its purpose is export.

If NavBarComponent is used app wide so it should be in CoreModule. Not in SharedModule.

If you do not have to, don't import SharedModule into AppModule. SharedModule is for FeaturedModules.

Read Offical Docs: https://angular.io/guide/ngmodule-faq#what-kinds-of-modules-should-i-have-and-how-should-i-use-them

3 Comments

And why should SharedModule not have any providers ? What if I want to have a new service for every feature ? what's wrong with that ?
If you have a feature specific service you should put it in that feature module. Not in shared module. Shared module is for all.
Care to explain your statement ?
0
  1. create a shared module in "src\app" using: ng g module shared

  2. import all the needed material modules in the shared module. important!!! sometimes you get an error if you tried to import set of modules together. you should import them one by one like here below=>

    import { MatButtonModule } from '@angular/material/button';

    import {MatFormFieldModule} from '@angular/material/form-field';

    import {MatInputModule} from '@angular/material/input';

then

    @NgModule({
  declarations: [],
  imports: [
    CommonModule,
    MatButtonModule,
    MatFormFieldModule,
    MatInputModule
  ],
  exports: [
    MatButtonModule,
    MatFormFieldModule,
    MatInputModule

  ]
})

now you are able to import this shared module wherever you need one or more of the material modules you imported in shared module . for example if you need angular material in the student example =>

import {SharedModule} from '../shared/shared.module'

and then

@NgModule({
  declarations: [
    TdFormComponent],
  imports: [
    CommonModule,
    SharedModule,
    
  ]
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.