Как обеспечить сервис на уровне модуля?

Я пытаюсь создать поставщика для библиотечного модуля, который при добавлении запускает моего поставщика фабрики. Однако когда я это делаю, провайдер никогда не выполняет console.info, поэтому я знаю, что он не работает...

Вот пример службы, экземпляр которой я пытаюсь создать:

@Injectable()
export class ExampleService {
  addOptions(...options: any[]) {
    // Set options
  }
}

Когда модуль будет создан, я хотел бы создать сервис и добавить к нему некоторые параметры по умолчанию, поэтому я создал поставщика фабрики вот так, так как я не хочу добавлять его на уровне компонента или для каждого экземпляра ExampleService . Это всего лишь одноразовая вещь для этого модуля.

@NgModule({
  declarations: [ExampleComponent],
  exports: [ExampleComponent],
  providers: [
    {
      provide: ExampleService,
      multi: false,
      useFactory: () => {
        console.info('Creating Example Service...');
        const service = new ExampleService();
        service.addOptions('one', 'two', 'three');
        return service;
      }
    }
  ],
})
export class LibraryModule {}

У меня также есть компонент, в который я хочу внедрить службу, но не использовать тот же экземпляр, что и в модуле, поэтому я сделал это:

@Component({
  providers: [ExampleService]
})
export class ExampleComponent {
  constructor(private example: ExampleService) {}
}

Когда команда в моей компании использует нашу библиотеку, они импортируют ее вот так, и параметры автоматически устанавливаются из файла LibraryModule.

@NgModule({
  imports: [LibraryModule]
})
export class App {}

В настоящее время тот, что в компоненте, создается как задумано, но тот, что в модуле, не запускает фабрику, поскольку я не вижу вывода из console.info. Что мне нужно сделать, чтобы это исправить? Или это не правильный способ сделать это?


Примечание. Я тестирую это с помощью сборника рассказов, но не уверен, имеет ли это значение.

Что выглядит так:

@Component({
  standalone: true,
  imports: [LibraryModule]
})
export class StorybookExample {
  constructor(private example: ExampleService) {}
}

export default {
  title: 'Test',
  component: StorybookExample,
} as Meta<StorybookExample>;
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Angular создает экземпляры сервисов, когда они используются, то есть когда он создает что-то, во что внедряется этот сервис. Именно поэтому просто указать фабрику недостаточно — фабрика вызывается только тогда, когда где-то используется сервис.

Что вы можете сделать, так это внедрить сервис в конструктор модуля:

@NgModule({
  declarations: [ExampleComponent],
  exports: [ExampleComponent],
  providers: [ExampleService],
})
export class LibraryModule {
  constructor(exampleService: ExampleService) {
    service.addOptions('one', 'two', 'three');
  }
}

Имейте в виду, что импорт этого модуля не приводит к принудительному созданию нового экземпляра — если сервис был предоставлен и внедрен где-то еще, возможно, уже существует экземпляр, который будет передан конструктору модуля. Вот почему наличие provideIn: 'root' у декоратора сервиса не влияет на количество создаваемых экземпляров.

Это поведение меняется для модулей с отложенной загрузкой, поскольку они создают свой инжектор, поэтому, если этот модуль импортируется как часть модуля с отложенной загрузкой, создается новый экземпляр для области действия модуля с отложенной загрузкой.

Детская площадка


Если это выглядит беспорядочно, так оно и есть. Не так давно Angular представил Standalone API, который позволяет создавать приложения вообще без использования NgModule. И есть специальный токен DI, который может быть использован пользователем для выполнения логики инициализации:

  providers: [
    {
      provide: ENVIRONMENT_INITIALIZER,
      multi: true,
      useValue: () => {
        inject(ExampleService).addOptions('one', 'two', 'three');
      }
    }
  ]

Это можно указать для bootstrapApplication или для конфигурации маршрута.


Возможно, на самом деле необходим способ передать некоторую конфигурацию службе. В таких случаях может быть лучше ввести InjectionToken, который затем можно будет использовать для предоставления конфигурации.

export const ExampleOptions = new InjectionToken<string[]>('Options for Example Service');

@Injectable({
  provideIn: 'root'
})
export class ExampleService {
  options = inject(ExampleOptions);
}

И использование:

// "Global"/"Default" configuration
bootstrapApplication(AppComponent, {
  providers: [{
    provide: ExampleOptions,
    useValue: ['one', 'two', 'three']
  }]
})

// Override for component's scope
@Component({
  /* ... */
  providers: [
    {
      provide: ExampleOptions,
      useValue: ['one', 'two', 'three']
    },
    /* Service should also be provided as Angular is not able to
       understand our intent to create a new instance 
       with a different set of options */
    ExampleService  
  ]
})
class MyComponent() {}

Наконец, чтобы сделать его менее многословным, мы могли бы создать фабрику поставщиков, аналогично некоторым API Angular, например. предоставить Маршрутизатор:

export provideExampleService = (options?: string[]) => [
  options 
    ? { provide: ExampleOptions, useValue: options }
    : [],
  ExampleService
];

Однако использование такого способа ведения дел не позволит использовать вариант использования, о котором просит ОП. Он говорит: «Когда команда в моей компании использует нашу библиотеку, они импортируют ее вот так, и параметры автоматически устанавливаются из LibraryModule». Первое решение жестко закодирует параметры в сервисе, не позволяя потребителям библиотеки самостоятельно настраивать сервис в корне. Кроме того, ключевым отличием вашей игровой площадки является то, что вы используете providedIn: 'root' в своем сервисе, а ОП — нет. Я думаю, вам следует указать на эту разницу и объяснить.

Geo242 22.03.2024 19:42

Где ОП говорит, что ему нужна конфигурация на корневом уровне (для этого потребуется другой подход)? Они все равно смогут настроить его после предоставления на уровне компонента, поскольку это будет новый экземпляр. providedIn: 'root' не имеет значения, когда создается экземпляр службы, единственная разница в том, что я могу пропустить ее предоставление в модуле, а для библиотеки это означает, что поставщик будет доступен, если в приложении присутствует какой-либо es-импорт библиотеки, без необходимости импортировать NgModule, что обычно нежелательно, но и не причиняет вреда

Shlang 22.03.2024 21:12

Я согласен со всем этим, я просто говорю, что ваш пост должен был включать это, потому что я не думаю, что ОП это понимает.

Geo242 23.03.2024 05:28

Другие вопросы по теме