1

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>
georgeawg
  • 48,608
  • 13
  • 72
  • 95
maxeber
  • 71
  • 1
  • 10

4 Answers4

0

Try this:

export class PanelComponent {
  title = 'default title';
  @ContentChild(LikeComponent) like: LikeComponent;

  ngAfterContentInit() {
    this.title = this.like.title;
  }
}
Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • I would really like to use your code but since I want to reuse the `PanelComponent` to wrap every other component I want, this won't work. – maxeber Jun 16 '16 at 22:20
0

If you want to use the event approach, you can use element.dispatchEvent to create a bubbling event. EventEmitter events don't bubble and should only be used with (xxx) binding.

In the child component this would look like

  constructor(private elRef:ElementRef, private renderer:Renderer) {}

  onBlur($event) {
    this.renderer.invokeElementMethod(this.elRef.nativeElement, 
        'dispatchEvent', 
        [new CustomEvent('input-blur', { bubbles: true })]);

See also my similar answer in Angular2 how to know when ANY form input field lost focus

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Well I really don't want to use event, it just seemed to me a possible approach. I only want to acces a child property from a parent who don't know if the child as let say a `title` property. I want to try it so if it return a string – maxeber Jun 18 '16 at 04:09
0

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>
jladan
  • 807
  • 7
  • 6
-1

i think this should do it in the panel template

<h2>{{title}}</h2>
<panel class="col-xs-6 col-md-4">
    <like (sendTitle)="receiveTitle($event. title)"></like>
</panel>

and set this in the component :

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

you send Output events like you do (click) events. () means that you submit something from the html to the component, [] means you bind something from the component to the html.

hope this is what you mean !

it's not really clear why you want to use ng-content ? everytime you click, the parent title will be updated from the child component.

L.querter
  • 2,332
  • 2
  • 13
  • 23
  • I am using `` because I want this component to be usable with any other component I want. In brief, it just wrap a component in a nice panel, but i'd like to get the `panel-title` from the child. From `LikeComponent`, I though I could use `OnInit() { this.sendTitle.emit({title: this.title}); }` so It would send the title when it is initiated ? Or should I send it through the `constructor`. – maxeber Jun 16 '16 at 22:16
  • 1
    I currently don't see a way without support from the user of panel. If the user adds a template variable or an attribute (to target it with a directive) then it can be queried using `@ContentChildren()`. I assume the Angular team will improve this eventually. You are not the first one to ask for this feature. – Günter Zöchbauer Jun 18 '16 at 05:28