Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject services of dynamically created components

Tags:

angular

export interface InfoPanelComponent {
    data: any;
}

export class ComplaintsComponent implements OnInit, InfoPanelComponent {
   data: any;             
   constructor(private navigationService: NavigationService, private activatedRoute: ActivatedRoute) {
   }

  onCancelClicked() {
      console.log(this.navigationService);
      this.navigationService.hidePanel(this.data.key, this.activatedRoute);
  }
}

The problem is, that this.navigationService is undefined. The service itself is provided in the app.modules.ts and can be injected in other components successfully.

I suppose the reason that it doesn't work is that the component above (and more components in future) is created dynamically:

private addPanel(item: InfoPanelItem, itemKey: string): void {
      const componentFactory = this.componentFactoryResolver.resolveComponentFactory(item.component);
      const viewContainerRef = this.infopanelHost.viewContainerRef;

      // create new component
      const componentRef = viewContainerRef.createComponent(componentFactory);

      (<InfoPanelComponent>componentRef.instance).data = item.data;   
  }

I suppose that the services are not being injected because of that. Is there a way to generically inject all dependancies that the component requires? Note that the ComplaintsComponent is just one example and there are more to come, that may require different services.

like image 970
user66875 Avatar asked Mar 20 '18 14:03

user66875


People also ask

Which will inject services into component class?

Angular provides the ability for you to inject a service into a component to give that component access to the service. The @Injectable() decorator defines a class as a service in Angular and allows Angular to inject it into a component as a dependency.

Which provider is used for dynamic dependency injection?

Factory providers: useFactory link The useFactory provider key lets you create a dependency object by calling a factory function, as in the following example. The injector provides the dependency value by invoking a factory function, that you provide as the value of the useFactory key.

What are dynamic angular components?

What dynamic components are. Dynamic means, that the components location in the application is not defined at buildtime. That means, that it is not used in any angular template. Instead, the component is instantiated and placed in the application at runtime.


1 Answers

You can provide services to your dynamic components in these two ways. In my example I will demonstrate creating the component inside a container component, DynamicContainerComponent.

First in the container component import all the services you will need. In my example AExampleService and BExampleService.

DynamicContainerComponent

import {
  Component, ViewChild, AfterContentInit, ComponentFactoryResolver, Compiler, ViewContainerRef, NgModule, NgModuleRef,
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AExampleService } from './services/AExampleService';
import { BExampleService } from './services/BExampleService';


@Component({
  selector: 'dynamic-container',
  template: `<ng-container #vc></ng-container>`,
  styleUrls: ['./dynamic-container.component.css']
})
export class DynamicContainerComponent implements AfterContentInit{
  @ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef;

  constructor(private aExampleService: AExampleService) { }

  ngAfterContentInit(){
    this.addDynamicComponent();
  }

  private addDynamicComponent(): void{...}
}

Now you can use the AExampleService and BExampleService by use of dependency injection in the dynamic component. This example shows two methods:

  • Direct DI of the service BExampleService into the dynamic component
  • Injecting the parent component DynamicContainerComponent into the dynamic component that will expose AExampleService.

Take a look at the code in private addDynamicComponent(): void{...}

addDynamicComponent()

private addDynamicComponent(): void{

  @Component({
    template: `<h2>This is a dynamic component</h2>`,
    styleUrls: ['./dynamic.component.css']
  })
  class DynamicComponent {
    constructor(
      public _parent: DynamicContainerComponent,
      private bExampleService: BExampleService) { }

    private someFunctionA(): void {
      this._parent.aExampleService.doSomthingA();
    }

    private someFunctionB(): void {
      this.bExampleService.doSomthingB();
    }
  }
  @NgModule({
    imports: [
      BrowserModule
    ],
    declarations: [DynamicComponent],
  }) class DynamicModule { }

  const mod = this.compiler.compileModuleAndAllComponentsSync(DynamicModule);
  const factory = mod.componentFactories.find((comp) =>
    comp.componentType === DynamicComponent
  );

  this.cmpRef = this._container.createComponent(factory);
}

Notice that neither AExampleService or BExampleService were declared as providers of the dynamic component, but rather they are declared as providers to module that contains the DynamicContainerComponent.

like image 191
Narm Avatar answered Oct 27 '22 00:10

Narm