angulardependency-injectionviewchild

Angular dynamically add component with injector to specify service


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?


Solution

  • 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
    });}