10

Cannot configure story for module where routerLink is used due to error

ERROR NullInjectorError: StaticInjectorError(DynamicModule)[RouterLinkActive -> Router]

To Reproduce Steps to reproduce the behavior: demo-app Run app and you can test that there is no possible solution to add RouterModule to work with story. It cannot be configured with RouterTestingModule, RouterModule, RouterModule.forRoot with iframe.html path. There is always the same error about missing provider.

Expected behavior To run application and story with routerLink

Additional context Latest version of storybook 5.3.3 and angular ~8.2.14 I was working with different configuration 5.2.8 and this issue does not appear.

How to configure this module, is there an issue in storybook?

Storybook issue

Adam Michalski
  • 1,722
  • 1
  • 17
  • 38

3 Answers3

12

Storybook works different than angular so I dont need to inject FeatureModule into story, Component is just fine. When only NavbarComponent is injected with RouterTestingModule

story configuration looks like this

storiesOf('Navbar', module)
  .addDecorator(
    moduleMetadata({
      imports: [BrowserAnimationsModule, RouterTestingModule],
      declarations: [NavbarComponent],
    }),
  )

And you don't need routes configuration xD

Adam Michalski
  • 1,722
  • 1
  • 17
  • 38
  • I still don't see the point in injecting `RouterTestingModule` which is supposed to be used in Unit tests and mock the routes, but it's okay if it works for you. – Ling Vu Feb 12 '20 at 16:44
  • 2
    This provides mocks for needed directives, and as storybook works it is only for presentation so full RouterModule is not required – Adam Michalski Feb 17 '20 at 15:27
7

I made the following changes:

  1. Adding the RouterModule including the the routes array (used to define your routes)
  2. Provide the required APP_BASE_HREF.

navbar.module.ts

const routes: Routes = [];


@NgModule({
  declarations: [NavbarComponent],
  imports: [
    CommonModule,
    RouterModule.forRoot(routes)
  ],
  providers: [{provide: APP_BASE_HREF, useValue: '/'}],
  exports: [NavbarComponent]
})
export class NavbarModule {
}

After that you just need to add the <router-outlet></router-outlet> into your NavbarComponent

Ling Vu
  • 4,740
  • 5
  • 24
  • 45
2

In case you are working with angular 15 and also want to render the router-outlet, I was able to solve it like this:

in preview.js:

const globalModuleImports = moduleMetadata({
  imports: [RouterTestingModule, RouterModule, HttpClientModule],
  providers: [Router],
});


const setRoutesMetadata = (fn, c) => {
  const story = fn();
  story.moduleMetadata.providers.push(
    {
      provide: ENVIRONMENT_INITIALIZER, multi: true, useValue() {
        inject(Router).resetConfig(c.parameters?.routes || [])
      }
    }
  )
  return story;
}

export const decorators = [
  globalModuleImports,
  setRoutesMetadata
];

and then in your story you can set routes inside a story parameters:

  SomeStory.parameters = {
    routes: [
      {
        path: "second",
        loadChildren: () => import('./second/second-routes'),
      },
    ]
  }