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

1.2k views Asked by At

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.

2

There are 2 answers

0
DaveLV2 On BEST ANSWER

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();
        ...
    }

2
Petr Averyanov On

Well actually all children can access parent providers, so simple:

@Component({providers: [FormService]})

in parent component does all you want.

For difference service instances, you can design it like this:

Add input for component array-item [service-config]="...". In component:

@Inject(FormService) public parentFormService: FormService
ngOnInit() {
     this.parentFormService.reConfigure(this.serviceConfig);
}

and in component children you can access this service.