Search
Write a publication
Pull to refresh
63.88
Skyeng
Крутейшая edtech-команда страны. Удаленная работа

Решаем ошибку при миграции на Storybook 7

Level of difficultyMedium
Reading time3 min
Views1.1K

Привет, я фронтенд-разработчик в Skyeng. При переходе с шестой версии Storybook на седьмую встретилась ошибка «Providers from the BrowserModule have already been loaded. If you need access to common directives such as NgIf and NgFor, import the CommonModule instead».

В этой небольшой статье объясняю, почему возникает баг и как его исправить.

Почему возникает ошибка

BrowserModule экспортирует инфраструктуру для всего Angular-приложения, и его стоит импортировать только один раз. Если повторно импортировать такой модуль, например, в lazy-loaded feature-модуль, то получим ошибку о необходимости использования CommonModule вместе целого BrowserModule.

По такому же принципу должны импортироваться и другие application-wide провайдеры в приложении.

Пример с ошибкой:

// feature.module.ts

@NgModule({
  imports: [BrowserModule], // здесь импортировать модуль не нужно
  …
})
export class FeatureModule {
…

// app.module.ts

@NgModule({
  imports: [BrowserModule], // AppModule уже импортирует BrowserModule
  …
})
export class AppModule {
…

Так как FeatureModule содержит повторный импорт BrowserModule, то получим ошибку: «Providers from the BrowserModule have already been loaded. If you need access to common directives such as NgIf and NgFor, import the CommonModule instead».

Что касается Storybook, то при аналогичном импорте модуля в метаданных так же возникнет ошибка:

// storybook-helper.module.ts

@NgModule({
  imports: [BrowserModule], // первая ошибка: StorybookHelperModule не должен импортировать BrowserModule
  …
})
export class StorybookHelperModule {
…

// test.stories.ts

export default {
  title: 'TestComponent',
  component: TestComponent,
  decorators: [
    moduleMetadata({
      imports: [StorybookHelperModule], // вторая ошибка: импортируем BrowserModule, находящийся в StorybookHelperModule
    }),
  ],

Даже если компонент в Storybook зависит от BrowserAnimationsModule / BrowserModule, такой модуль не стоит импортировать с помощью других модулей или напрямую при настройке Storybook.

В моём случае была ошибка именно в наличии импорта BrowserModule во вспомогательном модуле для сторибука. Но даже если BrowserModule импортирован напрямую в метаданных стори, то в консоли тоже будет сообщение о некорректном импорте BrowserModule, но в виде предупреждения.

Как исправить ошибку

Начиная с 7-й версии, Storybook использует новый bootstrapApplication API

Все application-wide провайдеры, включая BrowserModule и BrowserAnimationsModule, теперь настраиваются с помощью applicationConfig декоратора:

// test.stories.ts

export default {
  title: 'TestComponent',
  component: TestComponent,
  decorators: [
    applicationConfig({  // новый декоратор
      providers: [importProvidersFrom(BrowserAnimationsModule)],
    }),
  ],

То же самое касается конфигурируемых модулей (знакомый всем паттерн forRoot). Их тоже теперь указываем через applicationConfig декоратор.

Было:

export default {
  title: 'TestComponent',
  component: TestComponent,
  decorators: [
    moduleMetadata({
      imports: [SomeModule.forRoot()],
    }),
  ],

Стало:

export default {
  title: 'TestComponent',
  component: TestComponent,
  decorators: [
    applicationConfig({ // теперь используем новый декоратор
      providers: [importProvidersFrom(SomeModule.forRoot())],
    }),
  ]

Вывод

Стоит помнить про то, что содержит в себе модуль, для чего он нужен и как его правильно импортировать. Теперь с 7-й версии Storybook нужно использовать applicationConfig декоратор, если требуется использовать application-wide провайдеры или конфигурируемые модули.

Если остались вопросы — пишите, разберемся вместе!

Tags:
Hubs:
Total votes 13: ↑13 and ↓0+13
Comments0

Articles

Information

Website
www.skyeng.team
Registered
Founded
Employees
1,001–5,000 employees
Location
Россия
Representative
Alisa Kruglova