3

NOTE : I don't use Angular-Cli

OverlayModule seems to be required for other angular2-material components.

zone.js:355 Unhandled Promise rejection: Error in ./AppComponent class AppComponent - inline template:2:2 caused by: No provider for Overlay! ; Zone: ; Task: Promise.then ; Value:

I have installed all packages from angular2-material.

sytemjs.config.js

 map: {
      // our app is within the app folder
      app: 'dist',
      ...
      ...
      '@angular2-material/core': 'npm:@angular2-material/core/core.umd.js',
      '@angular2-material/button': 'npm:@angular2-material/button/button.umd.js',
      '@angular2-material/menu': 'npm:@angular2-material/menu/menu.umd.js',
      '@angular2-material/icon': 'npm:@angular2-material/icon/icon.umd.js',
      _____________________________________________________________________
      /*>>>>>   DO I NEED TO MAP ANYTHING FOR OVERLAY HERE?  <<<<<< */
      ______________________________________________________________________
      // other libraries
      'rxjs':                       'npm:rxjs',
      'angular2-in-memory-web-api': 'npm:angular2-in-memory-web-api',
    },

I'm using sharedModule for angular2-material components So that they will be available everywhere.

shared.module.ts

import {MdButtonModule } from '@angular2-material/button';
import {MdIconModule} from '@angular2-material/icon';
import {MdMenuModule} from '@angular2-material/menu';
import {MdIconRegistry} from '@angular2-material/icon';

@NgModule({
  imports:      [ CommonModule ],
  declarations: [],
  exports:      [ CommonModule,MdButtonModule,MdMenuModule,MdIconModule],
})
export class SharedModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: SharedModule,
      providers: [ MdIconRegistry ]  //>>>>> DO I NEED TO ADD ANYTHING HERE ??
    };
  }
}

App.Module.ts

@NgModule({
  imports:      [ BrowserModule,SharedModule.forRoot()],  
  ...
})

HTML:

<button md-icon-button [md-menu-trigger-for]="menu">
   <md-icon>more_vert</md-icon>
</button>

<md-menu #menu="mdMenu">
    <button md-menu-item> Refresh </button>
    <button md-menu-item> Settings </button>
    <button md-menu-item> Help </button>
    <button md-menu-item disabled> Sign Out </button>
</md-menu>
<button md-raised-button>Button</button>
micronyks
  • 54,797
  • 15
  • 112
  • 146
  • Can you post your html? – yurzui Sep 24 '16 at 14:39
  • Updated my question with HTML. – micronyks Sep 24 '16 at 14:43
  • https://github.com/jelbourn/material2-app/blob/master/src/app/app.module.ts here they import `OverlayModule` like : `import {OverlayModule} from '@angular2-material/core/overlay/overlay-directives';` If I try to do the same thing in sharedModule , it throws error like : `http://localhost:3000/node_modules/@angular2-material/core/core.umd.js/overlay/overlay-directives 404 (Not Found)`. I fell like I need to `map something` in `systemjs.config.js` but lately found that OverlayModule doesn't have `umd.js` So can't map anything in system file. – micronyks Sep 24 '16 at 14:50

2 Answers2

4

There are several options:

  • 1) Try to import OverlayModule in your shared module as shown below:

shared.module.ts

import {OverlayModule } from '@angular2-material/core';
@NgModule({
  imports:      [ CommonModule, OverlayModule.forRoot() ], <== here
  declarations: [],
  exports:      [ CommonModule,MdButtonModule,MdMenuModule,MdIconModule],
})
export class SharedModule {
  • 2) you can import it in main module

  • 3) or use the following:

shared.module.ts

static forRoot(): ModuleWithProviders {
  return {
    ngModule: SharedModule,
      [ MdIconRegistry,  MdMenuModule.forRoot().providers ]
  };
}

Plunker Example

  • 4) Or this way:

shared.module.ts

@NgModule({
  imports:      [ CommonModule, MdMenuModule.forRoot() ],
  declarations: [],
  exports:      [ CommonModule,MdButtonModule,MdMenuModule,MdIconModule],
})
  • 5) just add providers to your SharedModule

shared.module.ts

 import {OVERLAY_PROVIDERS } from '@angular2-material/core';
 return {
   ngModule: SharedModule,
   providers: [ MdIconRegistry,  OVERLAY_PROVIDERS ]
 };

forRoot() always return ModuleWithProviders object:

export interface ModuleWithProviders {
  ngModule: Type<any>;
  providers?: Provider[];
}

export class MdMenuModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: MdMenuModule,
      providers: OVERLAY_PROVIDERS,
    };
  }
}

https://github.com/angular/material2/blob/2.0.0-alpha.8/src/lib/menu/menu.ts#L21

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Just awesome. But why `import {OverlayModule } from '@angular2-material/core';` i don't get it. – micronyks Sep 24 '16 at 14:53
  • I tried to give you +2 but I can't :(. I need explanation on this. – micronyks Sep 24 '16 at 14:57
  • How do you know `MdMenuModule.forRoot().providers` ? any document or something? – micronyks Sep 24 '16 at 15:00
  • You shouldn't be adding to the providers or calling the forRoot in the shared module imports, for reasons [mentioned here](http://stackoverflow.com/a/39672927/2587435) – Paul Samsotha Sep 24 '16 at 15:07
  • @micronyks i guess `MdMenuModule.forRoot().providers` is the same as if you just import `OVERLAY_PROVIDERS` in put in providers – yurzui Sep 24 '16 at 15:17
  • but why import `{OverlayModule } from '@angular2-material/core';`? – micronyks Sep 24 '16 at 15:17
  • because it is public api https://github.com/angular/material2/blob/2.0.0-alpha.8/src/lib/core/core.ts#L35 and if you're using import from `core/overlay/overlay-directives` and bundle then it won't work – yurzui Sep 24 '16 at 15:24
  • the same thing happens where you use something like this `import {class} from @angular/core/src/..` and load `@angulat/core` as `umd.js` – yurzui Sep 24 '16 at 15:30
  • With this config your import `'@angular2-material/core/overlay/overlay-directives` will work https://gist.github.com/alexzuza/faf709f877c9adcc5e68ae6efbea403b – yurzui Sep 24 '16 at 15:42
2

Just to add to the accepted answer.

The first thing to mention is that forRoot() should not be imported into shared modules, for reasons mentioned here. Now should the providers from them be extracted out to add the shared modules @NgModule.providers

@NgModule({
  imports: [ SomeModule.forRoot() ],
  providers: [ SomeModule.forRoot().providers ]
})
export class SharedModule {}

Don't do either of those. Not in a shared module. Below is OK, though I don't think it's meant to be used this way

@NgModule({})
export class SharedModule {
  static forRoot() {
    return {
      ngModule: SharedModule,
      providers: [ SomeModule.forRoot().providers ]
    }
  }
}

Like I said it's OK, it'll work, but it just looks weird. Maybe a more elegant solution is the one provided by Material themselves. It you look at snapshot (as of now - will be in next release), you will see where they made a module that consolidated all the MD modules. Maybe just do what they are doing, but instead of adding all the modules, just add the ones you use

const MATERIAL_MODULES = [
  MdButtonModule,
  MdIconModule,
  MdMenuModule
];

@NgModule({
  imports: [
    MdButtonModule.forRoot(),
    MdIconModule.forRoot(),
    MdMenuModule.forRoot()
  ],
  exports: MATERIAL_MODULES
})
export class MaterialRootModule {}

@NgModule({
  imports: MATERIAL_MODULES,
  exports: MATERIAL_MODULES
})
export class MaterialModule {
  static forRoot() {
    ngModule: MaterialRootModule
  }
}

And in your shared module just

exports: [ MaterialModule ]

and in the app module

imports: [ MaterialModule.forRoot() ]

Style-wise, I think I would probably go this route.

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thank you for pointing this out.I offered to add `SomeModule.forRoot().providers` within forRoot method. With the first statement I agree. – yurzui Sep 24 '16 at 15:51