31

I'm writing angular components for the foundation css framework. I am working on the tabs component, and want to be able to pass some HTML to the <ng-content> of this.

The problem is, I also need to pass html which a user can put bindings on, like this:

PARENT TEMPLATE

<tabs [data]='example'>
    <div> Age <br> {{item.age}} </div>`
</tabs>

TABS COMPONENT

<ul class="tabs" #tabs>
  <li *ngFor="let item of data | async" (click)="tabClick($event)">
      <a>{{item.name}}</a>
  </li>
</ul>
<div>
  <ng-content></ng-content>
</div>

TABS TYPESCRIPT

@Component({
  selector: 'tabs',
  templateUrl: './tabs.component.html'
})

export class TabsComponent {
  @Input('data') data:any;
  @ViewChild('tabs') tabs: ElementRef;
}

Where item is a reference to an object in the example array.

However, I get this error: Cannot read property 'name' of undefined as item is being evaluated before it is inserted into the <ng-content> directive.

Is there a way to get around this limitation, or am I going about this the wrong way?

userqwert
  • 2,193
  • 5
  • 28
  • 50
  • to have a more look about the `ng-content` you can see this [**answer**](http://stackoverflow.com/questions/42735858/how-to-show-hide-bootstrap-modal-from-a-component/42736058#42736058) are you using ng2-bootstrap tabs or material design. you can also refer [**Material Design**](http://stackoverflow.com/questions/42539272/navigate-to-a-particular-tab-on-click-of-button-in-another-tab-angular2-material/42561544#42561544) for tabs example. – Aravind Mar 18 '17 at 18:51
  • I'm not using either of those, I am writing some custom components and directives for foundation 6 which don't exist yet, as I prefer that framework. Selectedindex looks useful, but not for this problem! – userqwert Mar 18 '17 at 18:54
  • what is your exact problem? where are you using `ng-content`? inside custom or the main component? Which is parent and which is child? – Aravind Mar 18 '17 at 18:55
  • I added the typescript to try and make things clearer – userqwert Mar 18 '17 at 18:58
  • 1
    What can't work in your code is that you use `item` outside of `*ngFor`, therefore accessing `item in `` can't work. I find it to confusing what you actually try to accomplish to think of a proper workaround. – Günter Zöchbauer Mar 18 '17 at 19:00

3 Answers3

49

update Angular 5

ngOutletContext was renamed to ngTemplateOutletContext

See also https://github.com/angular/angular/blob/master/CHANGELOG.md#500-beta5-2017-08-29

original

ngTemplateOutlet or ngForTemplate can be used for that use case:

<tabs [data]='example'>
  <ng-template let-item>
    <div> Age <br> {{item.age}} </div>`
  </ng-template>
</tabs>
@Component({
  ...
  template: `
    <ul class="tabs" #tabs>
      <li *ngFor="let item of data | async" (click)="tabClick($event)">
          <a>{{item.name}}</a>
      </li>
    </ul>
    <div>
      <ng-template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{$implicit: (data | async)}"></ng-template>
    </div>
  `
})
class TabsComponent {
  @ContentChild(TemplateRef) templateRef:TemplateRef;
}

See also Angular 2 bind transcluded content to loop variable

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
1

You should be using this way instead,

<tabs [data]='example'>
    <div> Age <br> {{item.age}} </div>`
</tabs>

Component typescript

@Component({
  selector: 'tabs',
  templateUrl: './tabs.component.html'
})

export class TabsComponent {
  @Input() data:any;
  item:any{};
}

In your content projection define a selector as

 <div class="tabs-body">
        <ng-content select=".tabs-body"> </ng-content>
      </div>

As your passing with bindings

<tabs [data]='example'>
    <div> Age <br> {{item.age}} </div>`
</tabs>

DEMO

Aravind
  • 40,391
  • 16
  • 91
  • 110
-6

You need to pass the item object to the ng-content component.

<ng-content [item]="selectedTab></ng-content>

I am not certain on what lies behind the tab click event but you can assign that item object to selectedTab which will be passed to the component.

The component that will control the tab view can have the following:

@Input() item: Item;

And this will pass that object when you click. I might be attacking this from the wrong angle but maybe it will help you in some way.