Как я могу получить доступ к методу "select" из NgbTabSet в компоненте в Angular2 и ng-bootstrap?

Использование Angular 2.3.1 и ng-bootstrap 1.0.0-alpha.18. Я пытаюсь программно выбрать вкладку на основе идентификатора из компонента, а не из шаблона. Цель состоит в том, чтобы извлечь параметр из URL и использовать его для выбора вкладки в ngOnInit.

шаблон

<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>

И код компонента:

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');
   }
}

Это просто выдает ошибку:

Ошибки журнала консоли

Как я могу получить доступ к этому методу?

Ответ 1

Маршрутизация с помощью Ngb-TabSet

В AngularJs 1.x с использованием ui-router настройка маршрутов имен была прямой. В Angular 2 с Ng-Bootstrap это не так очевидно. С другой стороны, то, что вам нужно, доступно в родных библиотеках Angular 2.

Настройка конфигурации маршрута

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'}
    ];

Конфигурация проста с одним исключением: атрибут [данные]. Вы заметите, что у него есть ключ под названием name. Это название маршрута. Подумайте об этом как атрибуте данных в качестве пакета данных. Вы можете добавить не просто имя маршрута.

Настройка разметки TabSet

<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>

В приведенной выше разметке вкладок нет ничего волшебного, есть пара вещей, которые вы хотите принять к сведению: сначала в элементе ngb-tabset, мы объявили переменную #tab. Хорошо использовать #tab позже в компоненте. Во-вторых, каждый nbg-tab имеет набор id, который соответствует имени, которое мы определили в конфигурации маршрута (т.е. 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);
        }
    } 
 }

Самая сложная часть этого упражнения заключалась в правильном выполнении порядка выполнения. Если это неверно, сбор или свойство не будут инициализированы до того, как вы начнете работать с ним. Хорошо начинайте с вершины класса и прокладывайте себе путь.

Во-первых, мы имеем переменные. prospectId является первичным ключом данных, selectedTab - это имя текущей выбранной вкладки, и, наконец, у нас есть переменная tabs. tabs является ссылкой на атрибут (#tab), который мы добавили в элемент ngb-tabset.

Далее находится constructor. Это не очевидно в документации, но data является Observable<data>. Чтобы зафиксировать значение, мы подписываемся на свойство data на маршруте.

Следующим constuctor является ngOnInit. Это не важно для вкладок, но оно захватывает перспективу, которую мы используем в маршрутизации вкладок. Если у вас нет динамических данных на ваших маршрутах, вам это не понадобится.

Наконец, мы имеем ngAfterViewChecked. Для маршрутизации tabs это самое важное. Здесь мы используем переменную tabs, которую мы захватили из разметки, и ее, где мы передаем выбранное имя вкладки в tabs, чтобы изменить выбранную вкладку.

Update

Чтобы это нормально работало, мне пришлось добавить, чтобы подключиться к событию tabChange на ngb-tabset.

HTML:

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

TypeScript:

Кроме того, мне пришлось жестко кодировать маршруты в функции onTabChange.

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

На случай, если кто-то захочет сделать это из шаблона, лучшим подходом будет:

<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>

Ответ 3

Это происходит потому, что вы вызываете select на вкладках, прежде чем вкладки даже были инициализированы. NgTabset инициализируется после того, как представление было init. Я использовал логическое значение, чтобы узнать, были ли они инициализированы до вызова select.

    tabsInitialized: boolean = false;
    @ViewChild('tabs') public tabs:NgbTabset;

    ngAfterViewInit() {
     this.tabsInitialized = true;
    }



    ngOnChanges(){
     if(this.tabsInitialized){
      this.tabs.select('dashboard');
    }
   }
...

Ответ 4

Поместите ссылку на элемент

<ngb-tabset #tabs>

Используйте ViewChild для управления вкладкой

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');
  }
}

Я переместил выполнение выбора в набор, чтобы быть уверенным, что страница правильно создала компонент вкладки, и на нее можно ссылаться и использовать.

Ответ 5

Проблема в том, что вы выполняете код в неправильной части цикла обработки событий. Вы можете использовать await/async чтобы перенести настройку активной вкладки на более позднюю часть цикла событий с другими микро-задачами.

маршрут настроен .ts

// ... other stuff
{ path: ":id/:tabName", component: ViewTabsComponent },
// ... other stuff

HTML/разметка

<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>

компонентный контроллер 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}');
}

это работает с

  • "@ng-bootstrap/ng-bootstrap": "^ 5.0.0",
  • "@angular/common": "^ 8.0.0",