问题
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?
回答1:
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]);
}
回答2:
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');
}
}
...
回答3:
In case anyone wants to do it from the template, the best approach is :
<ngb-tabset #tabRef="ngbTabset">
<ngb-tab title="Tab-1" id="tab1">
<ng-template ngbTabContent>
<p> Tab 1 Content</p>
</ng-template>
</ngb-tab>
<ngb-tab title="Tab-2" id="tab2">
<ng-template ngbTabContent>
<p> Tab 2 Content</p>
</ng-template>
</ngb-tab>
</ngb-tabset>
<div>
<button class="btn" (click)="tabRef.select('tab2')">Select tab with id tab2</button>
</div>
回答4:
Put a reference on the element
<ngb-tabset #tabs>
Use a ViewChild to control the tab
export class PolicyTermsComponent implements OnInit {
private tabs:NgbTabset;
@ViewChild('tabs') public set _tabs(tabs: NgbTabset)
{
if(!tabs) return;
this.tabs = _tabs;
this.tabs.select('policy');
}
}
I moved the execution of the select to a set so to be sure that the page properly created the tabs component and it can be referenced and used.
回答5:
The problem you are having is because you are executing the code in the wrong part of the event loop. You can use the await/async
to push the setting of the active tab to later in the event loop with other micro-tasks
route set up .ts
// ... other stuff
{ path: ":id/:tabName", component: ViewTabsComponent },
// ... other stuff
html / markup
<ngb-tabset #myTabs>
<ngb-tab title="Terms and Privacy" id="terms">
<template ngbTabContent>
<p>content</p>
</template>
</ngb-tab>
<ngb-tab title="Company Policy" id="policy">
<template ngbTabContent>
<p>other content<\p>
</template>
</ngb-tab>
</ngb-tabset>
component controler ts
@ViewChild("myTabs", { static: true, read: NgbTabset }) myTabs: NgbTabset;
async ngOnInit() {
const params = await this.route.paramMap
.pipe(
map((params: ParamMap) => ({ tabName: params.get("tabName") })),
take(1) // <-- force to complete
).toPromise();
this.myTabs.select(`${params.tabName}`);
}
this is working with
- "@ng-bootstrap/ng-bootstrap": "^5.0.0",
- "@angular/common": "^8.0.0",
来源:https://stackoverflow.com/questions/42446434/how-can-i-access-the-select-method-from-ngbtabset-in-the-component-in-angular2