Lee Winder Lee Winder - 3 months ago 25
HTML Question

Angular 2 - Injecting a component created by a HTML selector

I am trying to access a component that has been created using a selector within some HTML. I was under the impression (wrongly it seems) that providers would look for an existing instance and provide that when injected into another component, but obviously I am misunderstanding the hierarchical provider creation process.

In the following, I have a component, which uses a selector within its template HTML to create an instance of TopLevelComponent.

I am trying to access that TopLevelComponent by creating a provider and using DI to push that instance through the constructor.

(Apologies if this code is not perfect, I have just put together a quick example.)

@component({
selector: 'my-app',
template: '<top-level-component></top-level-component>',

directives: [TopLevelComponent],
providers: [TopLevelComponent],
})
export class MyApp {

constructor( private topLevelComponent: TopLevelComponent) {

}
}

@component({
selector: 'top-level-component',
template: '',
})
export class TopLevelComponent {

constructor() {
console.log('CONSTRUCTED A TopLevelComponent...');
}

}


But, instead of getting one instance of TopLevelComponent passed to my component I get two (as evidenced by the two logs of 'CONSTRUCTED A TopLevelComponent...' present in the debug log).

If I remove 'private topLevelComponent: TopLevelComponent' from the constructor, I only get one instance of the component, but I don't seem to be able to get hold of this.

So I have two questions


  • How do I use DI to pass in the instance of the selector created component to the constructor of other components



or


  • If I don't include
    <top-level-component></top-level-component>
    in the app HTML, how can I inject that HTML into the app so it renders correctly?



And a third


  • If both of the above are possible, which is the recommended method?



I was thinking @ViewChild would be the right method, as there will(!) only be one instance of this component, but as it's injected into a number of different components, I don't think that's possible.

Answer

If you add a component to providers: [...] then the component is treated like a plain class. If DI finds such a provider when TopLevelComponent is requested it creates an instance of the component class @Component(...) decorator is ignored.

If a component is listed in directives: [...] DI will find them as components and directives if they were instantiated because of matching selectors.

Update

  <component-to-inject #source></component-to-inject>
  <component-to-receive [injectedComponent]="source"></component-to-receive>

with

export class ReceivingComponent { 

  @Input() injectedComponent: InjectingComponent;

  ngOnInit() {
    console.log(this.injectedComponent);
  }      
}