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?
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.
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.
<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'}
).
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.
To get this working properly, I had to add to hook into the tabChange
event on the ngb-tabset
.
<ngb-tabset [activeId]="selectedTab" #tabs (tabChange)="onTabChange($event)">
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]);
}
This is happening because you are calling select on tabs before tabs have even been initialized. NgTabset are initialized after view has been init. I used a boolean to see if they have been initialized before calling select.
tabsInitialized: boolean = false;
@ViewChild('tabs') public tabs:NgbTabset;
ngAfterViewInit() {
this.tabsInitialized = true;
}
ngOnChanges(){
if(this.tabsInitialized){
this.tabs.select('dashboard');
}
}
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With