angularangular-servicesangular-injector

Pass service/provider for specific child component to use as inject value


FormService contains state for parts of form.

export class ArrayComponent implements OnInit {

   formServices: FormService[] = [];

   constructor(
        @Inject(FormService) public parentFormService: FormService,
    ) {
    }

   ngOnInit(): void {
      // Here I create lets say 10 FormService objects, that have to go to there separated child components.
      this.parentFormService.data$.subscribe((d: any[]) => d.foreach((v: any) => this.formServices.push(new FormService(v))))
   }
}

Lets imagine that based on some data, I create new FormService. I need to be able to pass this created FormService for a specific child component.

<ng-container *ngFor="let s of formServices">
   <array-item [inject/providers]="s"></array-item>
</ng-container>

Is there a way to pass this injectable service into a component as a Injectable?

You may be asking why would I need it to be injectable or why cant I just pass it in as @Input. The issue is that down in the tree of components there is for example a TextInputComponent that is trying to Inject a FormService to store state and do other Input things.

I pretty much want to do what @Component({providers: []}) does, but outside of the component, so I can directly control the provided Service and have access to it from Parent and Child component.

EDIT: // More precise thing i want to do simplified

<array-item [inject/providers]="formServices[0]"></array-item>
<array-item [inject/providers]="formServices[1]"></array-item>
<array-item [inject/providers]="formServices[2]"></array-item>
<array-item [inject/providers]="formServices[3]"></array-item>

and then down the line inside of array-item component there is a component that uses @Inject to inject FormService and use it.


Solution

  • This is very possible.

    For every child its possible to create an Injector like this -

    ...
    
    constructor(
            ...
            private injector: Injector,
            ...
        ) {
        }
    ...
    
    Injector.create({providers: [...providder here...], parent: this.injector})
    

    And then pass it to a component that renders further children like this -

    ...
    @Input() injector!: Injector;
    ...
        constructor(
            ...
            public viewContainerRef: ViewContainerRef,
            ...
        ) {
    
        }
    ...
    ngOnInit(): void {
            ...
            const component = this.viewContainerRef.createComponent(...Component..., {
                ...
                injector: this.injector
                ...
                }),
            });
            ... // Set @Input attributes here with component.instance.
            component.changeDetectorRef.detectChanges();
            ...
        }