Я пытаюсь создать поставщика для библиотечного модуля, который при добавлении запускает моего поставщика фабрики. Однако когда я это делаю, провайдер никогда не выполняет 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>;



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


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
];
Где ОП говорит, что ему нужна конфигурация на корневом уровне (для этого потребуется другой подход)? Они все равно смогут настроить его после предоставления на уровне компонента, поскольку это будет новый экземпляр. providedIn: 'root' не имеет значения, когда создается экземпляр службы, единственная разница в том, что я могу пропустить ее предоставление в модуле, а для библиотеки это означает, что поставщик будет доступен, если в приложении присутствует какой-либо es-импорт библиотеки, без необходимости импортировать NgModule, что обычно нежелательно, но и не причиняет вреда
Я согласен со всем этим, я просто говорю, что ваш пост должен был включать это, потому что я не думаю, что ОП это понимает.
Однако использование такого способа ведения дел не позволит использовать вариант использования, о котором просит ОП. Он говорит: «Когда команда в моей компании использует нашу библиотеку, они импортируют ее вот так, и параметры автоматически устанавливаются из
LibraryModule». Первое решение жестко закодирует параметры в сервисе, не позволяя потребителям библиотеки самостоятельно настраивать сервис в корне. Кроме того, ключевым отличием вашей игровой площадки является то, что вы используетеprovidedIn: 'root'в своем сервисе, а ОП — нет. Я думаю, вам следует указать на эту разницу и объяснить.