Angular dynamically add component with injector to specify service

657 Views Asked by At

I need to dynamically add some components in Angular 14, and I want to be able to control which implementation of an abstract service each component gets, based on some param I have available when I'm creating them.

I'm using ViewContainerRef.createComponent to add them, with a custom Injector that I can set the providers for. This works fine:

makeCustomInjector(param: string) {
return Injector.create({
    providers: [
        {
            provide: AbstractService,
            useFactory: (): AbstractService => {
                if (param === 'some_value') {
                    return new AbstractServiceImpA ();
                } else {
                    return new AbstractServiceImpB ();
                }
            }
        }]
});}

const injector: Injector = this.makeCustomInjector('param_value');
const componentRef = this.container.createComponent(DynamicallyAddedComponent, {
            injector, environmentInjector
        });

But I want to use services which have dependencies, so I add these to the provider for my injector like this:

makeCustomInjector(param: string) {
return Injector.create({
    providers: [
        {
            provide: AbstractService,
            useFactory: (serviceC: ServiceC): AbstractService => {
                if (param === 'some_value') {
                    return new AbstractServiceImpA (ServiceC);
                } else {
                    return new AbstractServiceImpB (ServiceC);
                }
            }, deps: [
                ServiceC
            ]
        }]
});}

But I can't get this to work without this error: NullInjectorError: No provider for ServiceC!

What am I doing wrong? Or is there a better way to achieve this?

1

There are 1 best solutions below

1
user814425 On BEST ANSWER

Got this working by adding a parent injector like this:

makeCustomInjector(param: string) {
return Injector.create({
    providers: [
        {
            provide: AbstractService,
            useFactory: (serviceC: ServiceC): AbstractService => {
                if (param === 'some_value') {
                    return new AbstractServiceImpA (ServiceC);
                } else {
                    return new AbstractServiceImpB (ServiceC);
                }
            }, deps: [
                ServiceC
            ]
        }], 
parent: this.appRef.injector
});}