0

I want to create custom Tabs component that will be able to display contents of [root] inside itself. It works perfectly when I use selectors in html tags (<tab1>), but I do not always know what the selector would be. Tabs are basically components so I need a way to render component to the view

My Tab.ts which should display the root component inside viewport

@Component({
    selector: 'tab',
    template: 
     `<div [hidden]="!active">
          <ng-content></ng-content>
     </div>`
})
export class Tab {
    @Input('tabTitle') title: string;
    @Input() active = false;
    @Input('root') root: any;
}

Then here is Tabs.ts which glues tabs together (enables you to switch between them)

@Component({
    selector: 'tabs',
    template:
      `<div class="tabbar">
        <div class="tab-button" *ngFor="let tab of tabs"  (click)="selectTab(tab)">
          <a href="#">{{tab.title}}</a>
        </div>
      </div>

      <ng-content></ng-content>`
})

export class Tabs implements AfterContentInit {
  @ContentChildren(Tab) tabs: QueryList<Tab>;

  ngAfterContentInit() {
    let activeTabs = this.tabs.filter((tab) => tab.active);
    if (activeTabs.length === 0) {
      this.selectTab(this.tabs.first);
    }
  }

  selectTab(tab: Tab) {
    this.tabs.forEach(tab => tab.active = false);
    tab.active = true;
    if (this.menu) {
      this.menu.active = false;
    }
  }
}

Then I have this template that should enable me to use tabs and works in all cases

<tabs>
  <tab tabTitle="Tab 1">
    <tab-home></tab-home>
  </tab>
  <tab tabTitle="Tab 2">
    <tab-second></tab-second>
  </tab>
  <tab tabTitle="Tab 3">
    Tab 3 content
  </tab>
</tabs>

But at the end I would like do be able to do it like this:

<tabs>
  <tab *ngFor="let tab of tabs" [title]="tab.title">      
    {{ tab.component }}
  </tab>
</tabs>

Instead of tab.component I need to show the rendered view of component but without selector it doesn't seem to work.

So I need a way to inject rendered view from component into template without directive (as I never know what the directive will be)

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

2 Answers2

0

You can use a local variable instead of a directive for @ContentChildren

<some-component #tabItem></some-component>

Then pass the variable name to ContentChildren as a string:

@ContentChildren('tabItem') 
dovidweisz
  • 1,215
  • 13
  • 24
0

At the end I have ended up with slightly modified solution posted by Günter Zöchbauer from this answer: Angular 2 dynamic tabs with user-click chosen components

I have used wapsee's solution to get html tag into typescript but I used @ViewChild as I have it inside component template.

I only modified the code for Tab.ts which now looks like this:

@Component
  selector: `tab`,
  template: 
    `<div [hidden]="!active">
      <div #viewport></div>
      <div class="nav-decor"></div>
    </div>`
})
export class Tab {
  @Input('tabTitle') title: string;
  @Input() active = false;
  @Input('tabIcon') icon: string;
  @Input() root: Type<Component> = DefaultTabComponent;

  componentRef: ComponentRef<Component>;
  @ViewChild('viewport', {read: ViewContainerRef}) target: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  ngAfterViewInit() {
    let factory = this.componentFactoryResolver.resolveComponentFactory(this.root);
    this.componentRef = this.target.createComponent(factory);
  }
}

And now I am finally able to inject list of objects as tabs using *ngFor

Community
  • 1
  • 1
zigab123
  • 1
  • 2