I'm using angular 5.2.2 and I'd like to know if it's possible to find a descendant component (nesting depth 2+).
For example:
FirstLevelComponent Template:
<first-level-component>
<second-level-component></second-level-component>
</first-level-component>
SecondLevelComponent Template:
<third-level-component></third-level-component>
I'd like to get ThirdLevelComponent instance in the FirstLevelComponent.
This plunker demonstrates what I'm trying to do: https://plnkr.co/edit/9NsYmrsJMJEi0wofQReI?p=preview
EDIT (2018/02/06)
One option could be inheriting SecondLevelComponent for exposing ThirdLevelComponent, but I wouldn't like to use inheritance and it would be limited to just one more depth level.
After some inspecting, I endend up creating a helper that searches for descendants components. It uses ViewContainerRef:
import * as NgCore from '@angular/core';
@NgCore.Injectable()
export class FindDescendantsService {
constructor() { }
private findRecursive(container: any, type: any, results: any[]) {
if (!container) { return; }
if ('nodes' in container) {
container.nodes.forEach(n => {
if ('componentView' in n && n.componentView) {
if (n.componentView.component instanceof type) {
results.push(n.componentView.component);
}
this.findRecursive(n.componentView, type, results);
return;
}
if ('viewContainer' in n && n.viewContainer && n.viewContainer._embeddedViews && n.viewContainer._embeddedViews.length) {
n.viewContainer._embeddedViews.forEach(v => this.findRecursive(v, type, results));
return;
}
});
}
}
find(vcr: NgCore.ViewContainerRef, type: any) {
var view = vcr['_view'];
var results = [];
this.findRecursive(view, type, results);
return results;
}
}
Then in FirstLevelComponent I can do:
ngAfterContentInit(){
var thirdLevelInstances = this.findDescendantsService.find(this.viewContainerRef, ThirdLevelComponent);
}
I'm not very comfortable because it uses some private variables that might change names/behaviour in future releases of angular, but this satisfy my goal for now. I'll wait some time to see if a better solution appears, if not, I think this helper might be the answer...
In order to do that, second component should have an instance of third component. E.g.
// First Component
@Component({
selector: 'first-comp',
template: `<second-comp></second-comp>`
})
export class FirstComponent {
@ViewChild(SecondComponent) secondComponent: SecondComponent;
doSomething() {
this.secondComponent.thirdComponent.doStuff();
}
}
// Second Component
@Component({
selector: 'second-comp',
template: `<third-comp></third-comp>`
})
export class SecondComponent{
@ViewChild(ThirdComponent) thirdComponent: ThirdComponent;
}
// Third Component
@Component({
selector: 'third-comp',
template: `<div>Hello World</div>`
})
export class ThirdComponent{
doStuff() {
// some logic here
}
}
However, I would not recommend this method. This creates tightly coupled components which is harder to test as well. You should always pass your data through @Input and @Outputs or some services.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With