maxeber maxeber - 1 month ago 11
AngularJS Question

Angular 2 : How do I send childComponent properties to parent when you don't know the childComponent's class?

Okay so here's what I'm trying to achieve :

I have this component :

import { Component, Output, EventEmitter, OnInit } from '@angular/core';

@Component({
selector: 'like',
template: '<p>this is the like component<p>'
})

export class LikeComponent implements OnInit{
title: string = 'Like Component';

@Output() sendTitle = new EventEmitter();

ngOnInit() {
this.sendTitle.emit({title: this.title});
}
}


I want to send the title from it to the parent :

import { Component, Input } from '@angular/core';

@Component({
moduleId: module.id,
selector: 'panel',
template: `
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">{{title}}</h2>
</div>
<div class="panel-body">
<ng-content (sendTitle) = "receiveTitle($event)"></ng-content>
</div>
</div>
`
})
export class PanelComponent {
title = 'default title';

receiveTitle(arg) {
this.title = arg.title;
console.log(arg);
}
}


Then I could reuse the
PanelComponent
with every component I want to wrap in a panel :

<panel>
<like></like>
</panel>

<panel>
<another-component></another-component>
</panel>

<panel>
<exemple-component></exemple-component>
</panel>


Each time a
Panel
component would be shown, it would try to get the title from the event if it exists.

It seems that this isn't working with ng-content as it is with regular parent/child component? I'm not familiar with angular 2 so I'd appreciate your feedback!

tl;dr : I want a way for a parent to know a properties from a child but the child musn't be specific (this is why I use
<ng-content>
).

In brief, I don't want to do it through
input
like this :

<panel [title] = "'This is the panel title of the like component'">
<like></like>
</panel>

<panel [title] = "'This is the panel title of the another-component'">
<another-component></another-component>
</panel>

Answer

After a lot of searching, I found a solution like Sasxa's, but able to work with any child component. The key is that the "selector" for @ComponentChild can be a string.

interface TitledComponent {
    title?: string;
}

@Component({
    selector: 'panel',
    template: `<div class="panel">
                    <h2>{{title}}</h2>
                    <ng-content></ng-content>
                </div>`
})
export class PanelComponent {
    title = 'default title';

    @ContentChild('titled') childComponent: TitledComponent;

    ngAfterContentInit() {
        if (this.childComponent)
            this.title = this.childComponent.title || 'default title';
    }
}

And the html would have to be

<panel>
    <like #titled></like>
</panel>
<panel>
    <other #titled></other>
</panel>
<panel>
    <p>I don't have a title</p>
</panel>
<panel>
    <like></like>
    <!-- This won't be picked up -->
</panel>