Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular - How to get nested component reference?

Tags:

angular

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 (Declared in my Application)
  • SecondLevelComponent (Third party component)
  • ThirdLevelComponent (Third party component)

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.

  • I've tried using @ViewChildren and @ContentChildren, but no success. Both returns an empty QueryList.
  • I can't change SecondLevelComponent code to expose the ThirdLevelComponent because it's a third party component.

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...

like image 946
Guilherme Meinlschmiedt Abdo Avatar asked Jun 30 '26 13:06

Guilherme Meinlschmiedt Abdo


1 Answers

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.

like image 187
Bunyamin Coskuner Avatar answered Jul 03 '26 07:07

Bunyamin Coskuner