storybook: [Bug]: NgModule is imported twice

Describe the bug

When an NgModule follows the forRoot pattern (https://angular.io/guide/singleton-services#prevent-reimport-of-the-greetingmodule), the module appears to be loaded twice and produces a runtime error indicating that the module was already loaded.

TestModule is already loaded. Import it in the AppModule only
Error: TestModule is already loaded. Import it in the AppModule only
    at new TestModule (http://localhost:6006/app-test-stories.iframe.bundle.js:61:13)
    at Object.TestModule_Factory [as useFactory] (ng:///TestModule/ɵfac.js:4:10)
    at Object.factory (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:43968:32)
    at R3Injector.hydrate (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:43886:29)
    at R3Injector.get (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:43787:23)
    at injectInjectorOnly (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:36619:29)
    at ɵɵinject (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:36623:59)
    at useValue (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:43585:25)
    at R3Injector.resolveInjectorInitializers (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:43827:9)
    at new EnvironmentNgModuleRefAdapter (http://localhost:6006/vendors-node_modules_storybook_addon-docs_angular_index_js-node_modules_storybook_addon-essen-170b5a.iframe.bundle.js:55368:14)

Note this pattern works fine in Angular 15 environment.

To Reproduce

// test.module.ts
import {
  ModuleWithProviders,
  NgModule,
  Optional,
  SkipSelf,
} from '@angular/core';

@NgModule()
export class TestModule {
  constructor(@Optional() @SkipSelf() parentModule?: TestModule) {
    if (parentModule) {
      throw new Error(
        'TestModule is already loaded. Import it in the AppModule only'
      );
    }
  }

  static forRoot(): ModuleWithProviders<TestModule> {
    return {
      ngModule: TestModule,
      providers: [],
    };
  }
}

// test.stories.ts
import { Component } from '@angular/core';
import { moduleMetadata, type Meta, type StoryFn } from '@storybook/angular';
import { TestModule } from './test.module';

@Component({
  selector: 'app-test',
  template: ``,
})
class TestComponent {}

export default {
  title: 'Module Test',
  component: TestComponent,
  decorators: [moduleMetadata({ imports: [TestModule.forRoot()] })],
} as Meta;

const Template: StoryFn<TestComponent> = (args: TestComponent) => ({
  props: args,
});

export const Test = Template.bind({});

System

Environment Info:

  System:
    OS: Windows 10 10.0.22621
    CPU: (24) x64 AMD Ryzen 9 3900X 12-Core Processor
  Binaries:
    Node: 18.14.2 - C:\dev\nodejs\node.EXE
    npm: 9.5.0 - C:\dev\nodejs\npm.CMD
  Browsers:
    Chrome: 110.0.5481.104
    Edge: Spartan (44.22621.1265.0), Chromium (110.0.1587.57)
  npmPackages:
    @storybook/addon-actions: 7.0.0-beta.55 => 7.0.0-beta.55
    @storybook/addon-essentials: 7.0.0-beta.55 => 7.0.0-beta.55
    @storybook/addon-interactions: 7.0.0-beta.55 => 7.0.0-beta.55
    @storybook/addon-links: 7.0.0-beta.55 => 7.0.0-beta.55
    @storybook/angular: 7.0.0-beta.55 => 7.0.0-beta.55
    @storybook/builder-webpack5: 7.0.0-beta.55 => 7.0.0-beta.55
    @storybook/testing-library: ^0.0.13 => 0.0.13

Additional context

Is it possible that the @SkipSelf is not being respected on the Module constructor parameter? Possibly related to this? https://github.com/storybookjs/storybook/issues/21243

Is it appropriate to import forRoot modules in the Story metadata? Do we need a “special” property for root imports perhaps?

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 20 (11 by maintainers)

Most upvoted comments