filipesilva / angular-quickstart-lib

MIT License
305 stars 75 forks source link

Library consumed in app build with angular-cli -> Error: No provider for Router! - when library has import of RouterModule and DI of Router #39

Closed kmolerov closed 7 years ago

kmolerov commented 7 years ago

I have build a component library based on angular-quickstart-lib and consumed it with app build with angular-cli. Everything works fine except when I try to create MenuComponent that has Router injected in it, or some other directive from RouterModule (library module has @NgModule({ imports: [RouterModule],
Everything works fine if MenuComponent is placed inside application code. I think that routerModule works with single instance application wide. My library package does not use the same instance. Maybe it is somehow related to the packaging of the library module or the metadata. I don't know how to solve this issue.

Everything uses angular 4.3.0 & angular-cli 1.2.1 (also have tried it with 4.2.6 and cli 1.2.0 before)

filipesilva commented 7 years ago

That doesn't sound like a packaging issue, but rather a problem with your DI. If you can provide me with a simple repro I can take a look.

@brandonroberts does this ring a bell to you?

kmolerov commented 7 years ago

I have a test case with very simple scenario. Quick steps to reproduce: -cloning this repo, npm install -add @angular/router (npm install @angular/router --save-dev), add it ot peerDependencies -import RouterModule in LibModule and configure child route like this: import { RouterModule, Routes } from '@angular/router'; @NgModule({ imports: [ RouterModule.forChild([{ path: 'lib', component: LibComponent }]) ], -npm run build
-create new app project with "ng new MyApp --routing=true" -copy library dist folder to application node-modules -import library in application module import { LibModule } from 'angular-quickstart-lib'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, LibModule // add this ], -ng server -in browser open http://localhost:4200/lib

result: core.es5.js:1020 ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'lib'

Problem is that RoutretModule.forChild(..) is not registered, or RouterModule that is imported in LibModule is different than the one imported in the App. Same scenario works if LibModule is inside the app.

Same problem happens if I inject Router inside some library component, but of course with different error (Error - No provider for Router!).

Maybe I am not registering something correctly. If steps to reproduce are not enough, tell me how to provide the code (but it will be more difficult as there is almost nothing changed in bare angular application build with ng new).

UPDATE: I think I tracked the problem to the generated library metadata.json. I have compare it with a library (that is working in project with ng-cli integration) that has Router injected in constructor (primeNg-BreadcrumbComponent) and two metadata.json files have differences. I will continue the investigation. I have to go into details what is the meaning of generated metadata code.

filipesilva commented 7 years ago

I can verify this happens but do not know why.

brandonroberts commented 7 years ago

@kmolerov the one thing you're missing is treating @angular/router as an external dependency. In the build.js file, you need to modify the global object and the external array like below.

      globals: {
        // The key here is library name, and the value is the the name of the global variable name
        // the window object.
        // See https://github.com/rollup/rollup/wiki/JavaScript-API#globals for more.
        '@angular/core': 'ng.core',
        '@angular/router': 'ng.router'
      },
      external: [
        // List of dependencies
        // See https://github.com/rollup/rollup/wiki/JavaScript-API#external for more.
        '@angular/core',
        '@angular/router'
      ],

Your current setup will cause the @angular/router and its dependencies to be bundled separately into your lib instead of using what's provided in the CLI project. That's why its getting a separate instance and looking for a different Router provider.

brandonroberts commented 7 years ago

README section for reference: https://github.com/filipesilva/angular-quickstart-lib#appendix-dependency-management

kmolerov commented 7 years ago

Thank, that solved the problem. I was struggling with this for 4 days. It seems I haven't red the README.md carefully. It is well explained there.

@brandonroberts thank you very much.

I can confirm that seed library is working great in project that has angular-cli integration. Also the build process is very simple and internals are easy to understand.

@filipesilva great work.

filipesilva commented 7 years ago

Cheers to @brandonroberts for figuring it out.

@kmolerov glad you're finding it useful. There's also a small CLI guide about using linked libs: https://github.com/angular/angular-cli/wiki/stories-linked-library.