How can I access the 'select' method from NgbTabSet in the component in Angular2 and ng-bootstrap?

bikeguy picture bikeguy · Feb 24, 2017 · Viewed 25.4k times · Source

Using Angular 2.3.1 and ng-bootstrap 1.0.0-alpha.18. I am trying to programmatically select a tab based on ID from the component rather than from within the template. The goal is to pull the param from the url and to use that to select the tab in ngOnInit

the template

<section id="policy-terms">
<ngb-tabset>
  <ngb-tab title="Terms and Privacy" id="terms">
    <template ngbTabContent>
      <div class="container page-content">

      </div>
    </template>
  </ngb-tab>
  <ngb-tab title="Company Policy" id="policy">
    <template ngbTabContent>
      <div class="container page-content">

      </div>
    </template>
  </ngb-tab>

</ngb-tabset>
</section>

And the component code:

import { Component, OnInit } from '@angular/core';
import { NgbTabset } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-policy-terms',
  templateUrl: './policy-terms.component.html',
  styleUrls: ['./policy-terms.component.scss'],
  providers: [
       NgbTabset
   ]
 })
 export class PolicyTermsComponent implements OnInit {

 constructor(
    public tabset: NgbTabset
  ) { }

  ngOnInit() {
    this.tabset.select('policy');
   }
}

This just produces an error:

Console log errors

How can I access this method?

Answer

Chuck Conway picture Chuck Conway · Jul 6, 2017

Routing with Ngb-TabSet

In AngularJs 1.x using ui-router setting up names routes was straight forward. In Angular 2 with Ng-Bootstrap it’s not as obvious. On the upside, what you need is available in native Angular 2 libraries.

Setting up the Route Configuration

export const appRoutes: Routes =
    [
        { path: 'prospect/:prospectid/details', component: ProspectTabsView, data:{name:'details'} },
        { path: 'prospect/:prospectid/appointments', component: ProspectTabsView, data:{name:'appointments'} },
        { path: 'prospect/:prospectid/followups', component: ProspectTabsView, data:{name:'followups'} },
        { path: 'prospect/:prospectid/contacts', component: ProspectTabsView, data:{name:'contacts'} },
        { path: '', component: DashboardView },
        { path: '**', redirectTo: '', pathMatch: 'full'}
    ];

The configuration is straightforward with one exception: the [data] attribute. You’ll notice it has a key called name. This is the name of the route. Think of it as a data attribute as a data-bag. You can add more than just the route name.

Setting up the TabSet Markup

<ngb-tabset #tabs>
    <ngb-tab id="details">
        <ng-template ngbTabTitle>
            <a [routerLink]="['/prospect', prospectId, 'details']">Details</a>
        </ng-template>
        <ng-template ngbTabContent>
        </ng-template>
    </ngb-tab>
    <ngb-tab id="contacts">
        <ng-template ngbTabTitle>
            <a [routerLink]="['/prospect',prospectId,'contacts']">Contacts</a>
        </ng-template>
        <ng-template ngbTabContent>
        </ng-template>
    </ngb-tab>
    <ngb-tab id="appointments">
        <ng-template ngbTabTitle>
            <a [routerLink]="['/prospect', prospectId, 'appointments']">Appointments</a>
        </ng-template>
        <ng-template ngbTabContent>
        </ng-template>
    </ngb-tab>
    <ngb-tab id="followups">
        <ng-template ngbTabTitle>
            <a [routerLink]="['/prospect', prospectId, 'followups']">Follow Ups</a>
        </ng-template>
        <ng-template ngbTabContent>
        </ng-template>
    </ngb-tab>
</ngb-tabset>

There is nothing magical about the above tab markup, there are couple things you want to take note of: first is in the ngb-tabset element, we’ve declared the variable #tab. We’ll use #tab later in the component. Second, each nbg-tab has an id set that matches name we defined in the route configuration (i.e. data:{name:'followups'}).

Setting up the Component

import {
     AfterViewChecked, Component, OnInit, 
    ViewChild
} from '@angular/core';
import '../../assets/css/styles.css';
import {ActivatedRoute} from "@angular/router";
import {NgbTabset} from "@ng-bootstrap/ng-bootstrap";

@Component({
    templateUrl: './tabs.view.html'
})
export class ProspectTabsView implements OnInit, AfterViewChecked{
    prospectId: number;
    selectedTab:string; 

    @ViewChild('tabs')
    private tabs:NgbTabset;

    constructor(private route: ActivatedRoute) { 
        this.route.data.subscribe(d=>{
            this.selectedTab = d.name;
        });
    }

    ngOnInit(): void {
        this.route.params.subscribe(
            params => {
                this.prospectId = +params['prospectid'];
            }
        );
    }

    ngAfterViewChecked(): void {
        if(this.tabs) {
            this.tabs.select(this.selectedTab);
        }
    } 
 }

The hardest part in this exercise was getting the order of execution correct. If it’s not correct, a collection or a property won’t be initialized before you operate on it. We’ll start at the top of the class and work our way down.

First, we have the variables. prospectId is the primary key of the data, selectedTab is the name of the currently selected tab, and lastly, we have the tabs variable. tabs is a reference to the attribute (#tab) we added to the ngb-tabset element.

Next is the constructor. It’s not obvious in the documentation, but data is an Observable<data>. To capture the value, we are subscribing to the data property from the route.

Following the constuctor is the ngOnInit. This isn’t important to the tabs, but it does capture the prospectId which we use in the tab’s routing. If you don’t have any dynamic data in your routes, then you don’t need this.

Lastly, we have ngAfterViewChecked. For routing the tabs, this is the most important. Here we use the tabs variable we captured from the markup and it’s where we pass the selected tab name to the tabs to change the selected tab.

Update

To get this working properly, I had to add to hook into the tabChange event on the ngb-tabset.

HTML:

<ngb-tabset [activeId]="selectedTab" #tabs (tabChange)="onTabChange($event)">

TypeScript:

Also, I had to hardcode the routes in the onTabChange function.

onTabChange($event: NgbTabChangeEvent) {
    let routes = {
        details: `/prospect/${this.prospectId}/details`,
        appointments: `/prospect/${this.prospectId}/appointments`,
        followups: `/prospect/${this.prospectId}/followups`,
        notes: `/prospect/${this.prospectId}/notes`,
        dials: `/prospect/${this.prospectId}/dials`,
    };

    this.router.navigateByUrl(routes[$event.nextId]);
}