Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

While clicking on tab i need to load particular component in angular2

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;
    }
  }
like image 521
Vignesh Avatar asked Mar 08 '23 12:03

Vignesh


2 Answers

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.

like image 95
Janith Widarshana Avatar answered Apr 10 '23 00:04

Janith Widarshana


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.

like image 36
Murphy4 Avatar answered Apr 09 '23 23:04

Murphy4