Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using NgRx (with ngrx-actions library) within an Angular 6 library

I am trying to split up an Angular application with multiple modules into multiple (separate) Angular libraries (using this guide), so I can use these libraries within multiple Angular applications.
Currently the project has several modules, for example:

  • One for authentication/authorization;
  • One containing all kind of table components
  • One containing all kind of filter components
  • One containing all kind of chart components
  • ... (I guess you get the point) ...

I started working on 'converting' the authentication/authorization module into a library, because I expected it would be the hardest and most complicated module.
Right now I am facing some problems which I will explain below.

The module (as it currently exists) makes use of NgRx in combination with ngrx-actions to reduce boilerplate. Like mentioned (in the guide), a library and an app for testing the library are created. I copied all the contents of the existing module into the library folder.

Had to make some adjustments, because the existing module reads some constants from environment.ts and a global.ts which will (probably) not exist when it's used as a library.

Right now my folder structure looks like the following, to give some more context (removed the node_modules):

.
├── README.md
├── angular.json
├── e2e
│   ├── protractor.conf.js
│   ├── src
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   └── tsconfig.e2e.json
├── package-lock.json
├── package.json
├── projects
│   ├── auth-lib
│   │   ├── karma.conf.js
│   │   ├── ng-package.json
│   │   ├── ng-package.prod.json
│   │   ├── package-lock.json
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── lib
│   │   │   │   ├── auth-check
│   │   │   │   │   ├── auth-check.component.html
│   │   │   │   │   └── auth-check.component.ts
│   │   │   │   ├── auth-guard.service.ts
│   │   │   │   ├── auth-routing.module.ts
│   │   │   │   ├── auth.module.ts
│   │   │   │   ├── auth.service.ts
│   │   │   │   ├── callback
│   │   │   │   │   ├── callback.component.ts
│   │   │   │   │   └── callback.html
│   │   │   │   ├── interceptors
│   │   │   │   │   └── auth.interceptor.ts
│   │   │   │   ├── permission-guard.service.ts
│   │   │   │   ├── signin
│   │   │   │   │   └── signin.component.ts
│   │   │   │   └── store
│   │   │   │       ├── auth-state.interface.ts
│   │   │   │       ├── auth.actions.ts
│   │   │   │       ├── auth.effects.ts
│   │   │   │       ├── auth.reducers.ts
│   │   │   │       └── auth.store.ts
│   │   │   ├── public_api.ts
│   │   │   └── test.ts
│   │   ├── tsconfig.lib.json
│   │   ├── tsconfig.spec.json
│   │   └── tslint.json
├── src
│   ├── app
│   │   ├── app.component.css
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   └── app.module.ts
│   ├── assets
│   ├── browserslist
│   ├── environments
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── favicon.ico
│   ├── index.html
│   ├── karma.conf.js
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.css
│   ├── test.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.spec.json
│   └── tslint.json
├── tsconfig.json
└── tslint.json

public_api.ts is containing the following code:

/*
 * Public API Surface of auth-lib
 */

export * from './lib/auth.module';
export * from './lib/auth-guard.service';
export * from './lib/permission-guard.service';
export * from './lib/auth.service';

auth.module.ts contains the following code:

import {NgModule} from '@angular/core';
import {SigninComponent} from './signin/signin.component';
import {AuthRoutingModule} from './auth-routing.module';
import {CallbackComponent} from './callback/callback.component';
import {AuthService, InternalAuthService} from './auth.service';
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {AuthInterceptor} from './interceptors/auth.interceptor';
import {AuthEffects} from './store/auth.effects';
import {EffectsModule} from '@ngrx/effects';
import {AuthStore} from './store/auth.store';
import {AuthCheckComponent} from './auth-check/auth-check.component';
import {NgrxActionsModule} from 'ngrx-actions/dist';
import {StoreRouterConnectingModule} from '@ngrx/router-store';

@NgModule({
  declarations: [
    SigninComponent,
    CallbackComponent,
    AuthCheckComponent
  ],
  imports: [
    AuthRoutingModule,
    NgrxActionsModule.forRoot({auth: AuthStore}),
    EffectsModule.forFeature([AuthEffects]),
  ],
  providers: [
    AuthService,
    InternalAuthService,
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
    StoreRouterConnectingModule,
  ],
  exports: [],
})
export class AuthModule {

}

NOTE: this is working when the module exists within a project (not as library).
FYI: The different components are dispatching some actions, like redirecting to the (Auth0) log in page.

Then.. when I want to import the AuthModule in the app.module.ts (the 'test application') I get the following error, which I can't solve.

Error: StaticInjectorError(AppModule)[NgrxActionsModule -> ReducerManager]: 
  StaticInjectorError(Platform: core)[NgrxActionsModule -> ReducerManager]: 
    NullInjectorError: No provider for ReducerManager!
    at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:1062)
    at resolveToken (core.js:1300)
    at tryResolveToken (core.js:1244)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:1141)
    at resolveToken (core.js:1300)
    at tryResolveToken (core.js:1244)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:1141)
    at resolveNgModuleDep (core.js:8376)
    at _createClass (core.js:8429)
    at _createProviderInstance (core.js:8393)

I saw this GitHub issue and this Stack Overflow question, both suggesting the same answer, but they are not working in my case.

Is there someone who knows how to fix this issue? Thanks!

like image 830
Stephan Schrijver Avatar asked Sep 06 '18 09:09

Stephan Schrijver


1 Answers

The suggestions in the Stack Overflow page and GitHub issue page I mentioned in my earlier post weren't enough to fix this problem, but helped me to 'solve' the problem or getting at least a bit further.

By importing the StoreModule.forRoot({}) another exception will be thrown:

Error: StaticInjectorError(AppModule)[AuthEffects -> Actions]: 
  StaticInjectorError(Platform: core)[AuthEffects -> Actions]: 
    NullInjectorError: No provider for Actions!
    at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:1062)
    at resolveToken (core.js:1300)
    at tryResolveToken (core.js:1244)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:1141)
    at resolveToken (core.js:1300)
    at tryResolveToken (core.js:1244)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:1141)
    at resolveNgModuleDep (core.js:8376)
    at _createClass (core.js:8425)
    at _createProviderInstance (core.js:8393)

After importing EffectsModule.forRoot([]) the following exception will be returned:

Error: StaticInjectorError(AppModule)[StoreRouterConnectingModule -> Router]: 
  StaticInjectorError(Platform: core)[StoreRouterConnectingModule -> Router]: 
    NullInjectorError: No provider for Router!
    at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:1062)
    at resolveToken (core.js:1300)
    at tryResolveToken (core.js:1244)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:1141)
    at resolveToken (core.js:1300)
    at tryResolveToken (core.js:1244)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:1141)
    at resolveNgModuleDep (core.js:8376)
    at _createClass (core.js:8425)
    at _createProviderInstance (core.js:8393)

By importing RouterModule.forRoot([]) it will (finally) work!
My auth.module.ts is now looking like this:

import {NgModule} from '@angular/core';
import {SigninComponent} from './signin/signin.component';
import {AuthRoutingModule} from './auth-routing.module';
import {CallbackComponent} from './callback/callback.component';
import {AuthService, InternalAuthService} from './auth.service';
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {AuthInterceptor} from './interceptors/auth.interceptor';
import {AuthEffects} from './store/auth.effects';
import {EffectsModule} from '@ngrx/effects';
import {AuthCheckComponent} from './auth-check/auth-check.component';
import {StoreRouterConnectingModule} from '@ngrx/router-store';
import {StoreModule} from '@ngrx/store';
import {RouterModule} from '@angular/router';
import {NgrxActionsModule} from 'ngrx-actions/dist';
import {AuthStore} from './store/auth.store';
import {StoreDevtoolsModule} from '@ngrx/store-devtools';

@NgModule({
  declarations: [
    SigninComponent,
    CallbackComponent,
    AuthCheckComponent
  ],
  imports: [
    RouterModule.forRoot([]),
    AuthRoutingModule,
    StoreModule.forRoot({}),
    NgrxActionsModule.forRoot({auth: AuthStore}),
    EffectsModule.forRoot([]),
    EffectsModule.forFeature([AuthEffects]),
    StoreRouterConnectingModule,
  ],
  providers: [
    AuthService,
    InternalAuthService,
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
  ],
  exports: [],
})
export class AuthModule {
}

One thing I have to test right now is if I will be able to add states and routes to the Angular application that's importing the auth library, because of importing multiple forRoot statements with an empty array in the library. I'll update this post after I have tested that!

like image 74
Stephan Schrijver Avatar answered Nov 17 '22 04:11

Stephan Schrijver