5

I am trying to load components in tab. while clicking on particular tab i need to load the particular component.But it's loading all component while navigating to that component.

.html

<p-tabView orientation="left" (onChange)="onTabChange($event)">
       <p-tabPanel *ngFor="let item of items" style="border: solid 1px; padding: 20px;margin: 20px;" [selected]="activeTabIndex==i">
              <strong> When you click here, 
              I should load the <span style="color:red"> {{item.name}} </span>
              component below</strong> <br />

              <ng-container *ngComponentOutlet="childmap[item.name] "></ng-container>

          <br />
        </p-tabPanel>
</p-tabView>

.ts

@Component({
  selector: 'my-app',
  templateUrl:'dashboard.html' 
  `
})
export class App {
 activeTabIndex: number = 0;

childmap = {
        'slider': sliderComponent,
        'user': usersComponent,
        'alert danger': AlertDangerComponent
         }


items:Array<any> = [
    {
      name: 'slider' 
    },
    {
      name: 'user'
    },
    {
      name: 'alert danger'
    }

      ]
 onTabChange(event: any) {
        this.activeTabIndex = event.index;
    }
  }
Vignesh
  • 2,378
  • 3
  • 25
  • 48

2 Answers2

4

What you are doing is basically trying to write your own router. The PrimeNg TabView is built to load the menu and all the tab contents first on initialization and then puts an artificial layer of tabs on top of it. However, under menu, there is TabMenu which is actually what you want. The key difference, is a menu allows your list to be MenuItems which can include routerLink as a parameter.

There is an excellent article here that explains the details which I prefer to the official documentation. You should understand this first, and then see how PrimeNg just implements a handy way to build these links for you if you supply routerLink. (See the MenuItems link for better documentation on the MenuItem model. From your code I can't tell if you are at your top level component or not, but I think what you are looking for is along these lines. This solution uses child routes which is slightly more complex, but a more useful example. This assumes you have AppComponent set up as your top level component with just <router-outlet></router-outlet> in the template. This will load your dashboard component as an empty root redirects to dashboard.

app.module.ts

const myDefinedRoutes: Routes = [
  {path: '', redirectTo: 'dashboard', pathMatch: 'full'},
  {path: 'dashboard', component: DashboardComponent, children: [
      {path: 'slider', component: SliderComponent},
      {path: 'user', component: UserComponent},
      {path: 'alert', component: AlertDangerComponent},
    ]
  }
]

@NgModule({
  // Add this line to imports
    RouterModule.forRoot(myDefinedRoutes)
})
export class AppModule {}

dashboard.component.ts

@Component({
  selector: 'my-app',
  template:`
   <p-tabMenu [model]="items"></p-tabMenu>
   <router-outlet></router-outlet>
  `
})
export class App implements OnInit {
  ngOnInit() {
    this.items = [
      {label: "Slider", routerLink: ['/dashboard', 'slider']},
      {label: "User", routerLink: ['/dashboard', 'user']},
      {label: "Danger", routerLink: ['/dashboard', 'alert']},
    ];
    this.activeItem = this.items[1]
  }
}

Make sure you include the router-outlet in your dashboard component too because the other components exist as children which means you will need another outlet because the first outlet is still housing the dashboard component, and inside the dashboard component, you are showing one of the other three components.

Murphy4
  • 1,499
  • 2
  • 15
  • 22
  • does it reload every time when we switch across the tabs? – Krishnan Dec 18 '17 at 21:19
  • If I understand your question right, yes. When using routing, each time the ActivatedRoute changes, the content of a router-outlet will rebuild the component inside the router-outlet calling ngOnInit and proceeding through the component lifecycle. – Murphy4 Dec 18 '17 at 22:42
  • thanks for your reply. I am facing similar requirement where I need to load dynamic components inside a angular tab. But i am facing some issues which I have explained in the post https://stackoverflow.com/questions/47879472/how-to-load-the-angular-components-dynamically-in-a-angular-tab ... If possible can you please help me in fixing this issue? – Krishnan Dec 19 '17 at 03:10
  • ng v7.2.0 ; primeng v7.0.5 ; primeicons v1.0.0 ... and this answer stills holds its stand. – j4v1 Feb 22 '19 at 01:41
4

There are many solution for this kind of things. Please What I have done by using ngComponentOutlet.

Here is the tab-container:

import {Component, Input} from '@angular/core'
import {TabContentAlternativeComponent} from './tab-content-alternative.component'
import {BasicContent} from './basic-content'

@Component({
  selector: 'tab',
  template: ''
})
export class TabComponent {
  @Input() title: string;
  @Input() contentRef: BasicContent;
  active = false;
}

This is a very simple component which knows its own tab name, an active state and the body component reference which should be loaded when somebody selects the tab.

Then we create several body components which will be loaded dynamically:

export class BasicContent {

}

Component 1

  import {Component, Input, OnInit} from '@angular/core'
import {BasicContent} from './basic-content'

@Component({
  selector: 'tab-content',
  template: `
      <p>Hey</p>
  `,
})
export class TabContentComponent extends BasicContent {
}

Component 2

   import {Component, Input} from '@angular/core'
import {BasicContent} from './basic-content'

@Component({
  selector: 'tab-content-alternative',
  template: `
      <p>Hey, this is an alternative content</p>
  `,
})
export class TabContentAlternativeComponent extends BasicContent {
}

Here is the tabs-container component with tabs rendering and an empty placeholder for dynamic body components:

    import {AfterContentInit, Component, ContentChildren, QueryList} from '@angular/core'
import {TabComponent} from './tab.component'
import {BasicContent} from 'basic-content'

import 'rxjs/Rx';
import {Observable, BehaviorSubject} from 'rxjs/Rx';

@Component({
  selector: 'tab-container',
  template: `
    <div class="tab-header">
      <div class="tab" *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active">{{tab.title}}</div>
    </div>

    <div class="tab-content">
      <ng-container *ngComponentOutlet="content | async"></ng-container>
    </div>
  `,
})
export class TabContainerComponent implements AfterContentInit {
  @ContentChildren(TabComponent) tabs: QueryList<TabComponent>;

  private contentSbj = new BehaviorSubject<BasicContent>(null);
  content = this.contentSbj.asObservable();

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

  selectTab(tab: TabComponent) {
    this.tabs.toArray().forEach(tab => tab.active = false);
    tab.active = true;
    this.contentSbj.next(tab.contentRef);
  }
}

TitleMapping

import {TabContentComponent} from './tab-content.component';
import {TabContentAlternativeComponent} from './tab-content-alternative.component';

interface TitleMapping {
  title: string;
  contentComponent: BasicContent;
}

export const allTabs: TitleMapping[] = [
  {title: "Tab 1", contentComponent: TabContentComponent},
  {title: "Tab 2", contentComponent: TabContentAlternativeComponent},
  {title: "Tab 3", contentComponent: TabContentComponent}
]

And this is how it can be used in some parent component:

import {TabContentComponent} from './tab/tab-content.component'
import {TabContentAlternativeComponent} from './tab/tab-content-alternative.component'

@Component({
  selector: 'my-app',
  template: `
    <tab-container>
      <tab title="Tab 1" [contentRef]="normalContent"></tab>
      <tab title="Tab 2" [contentRef]="alternativeContent"></tab>
    </tab-container>
  `,
})
export class App {
  normalContent = TabContentComponent;
  alternativeContent = TabContentAlternativeComponent;
}

Here is working Plunkr

I have used this with my projects and working fine as your requirement.

Janith Widarshana
  • 3,213
  • 9
  • 51
  • 73