Я хочу отделить интерфейс службы, используемой моими компонентами, от ее реализации (так случается, чтобы упростить создание способных ручных макетов) и внедрить реализацию в качестве зависимости для компонентов, которые ее используют. Этот подход является обычным для серверных частей Java Spring.
Итак, если у меня есть такой код:
export abstract class AbstractMyService {// Defines the interface of the service
...
}
export class MyService extends AbstractMyService {// The concrete implementation
...
}
@Component({
selector: 'app-myComponent',
templateUrl: './my.component.html',
styleUrls: ['./my.component.css']
})
export class MyComponent implements OnInit {
constructor(
private readonly service: AbstractMyService) {
}
...
}
Как убедиться, что когда MyComponent
создается экземпляр в приложении, его конструктору передается объект типа MyService
?
Я знаю, что если бы я не разделял интерфейс и реализацию (так что MyService
был конкретным классом, а AbstractMyService
не существовал), я мог бы просто сделать это так:
@Injectable({
providedIn: 'root'
})
export class MyService {
...
}
Мне просто нужно аннотировать MyService
как @Injectable
? Или я должен сделать что-то более сложное?
Вы должны явно объявить реализацию как поставщика
@NgModule({ // or @Component, depending on the place you want to configure.
....
providers: [{
provide: AbstractMyService,
useClass: MyService
}]
})
нет способа «автоопределить», как это делается в Spring. В этой части конфигурации явно сказано, что если что-то внедряет элемент с использованием токена AbstractMyService
, класс MyService
будет использоваться в качестве конструктора.
В общем случае, когда вы хотите, чтобы все компоненты использовали один и тот же конкретный класс, вы должны объявить этого провайдера для своего AppModule
. Для модульных тестов вы должны объявить поставщика как часть вашей конфигурации TestBed
:
TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [
...
{ provide: AbstractMyService, useFactory: () => { return new MockMyService(...); } }]
});