Search code examples
angularnavng-bootstrap

Ngb-tabset by default selects tab 1 when the [activeId] passed is not among the ngb-tab id. How to implement the same functionality in ngbNav?


`We have migrated from ngb-tabset to ngbnav and faced this issue. ngb-tabset has a default safe fallback tab -> first tab is selected when wrong activeId is passed. but for ngbNav when a wrong activeId is passed, no tab is selected.

Is there a way to solve this issue?

For example:

Below is the attached image.

ts file ts file:

import {Component} from '@angular/core';

@Component({
  selector: 'ngbd-tabset-basic',
  templateUrl: './tabset-basic.html'
})
export class NgbdTabsetBasic { 
  activeIdString:any="1";
  constructor(){
    this.activeIdString="not_a_valid_id"
  }
}

html file html file:

<ngb-tabset [activeId]="activeIdString">
  <ngb-tab title="Simple" id="1">
    <ng-template ngbTabContent>
      <p>Raw</p>
    </ng-template>
  </ngb-tab>
  <ngb-tab id="2">
    <ng-template ngbTabTitle ><b>Fancy</b> title</ng-template>
    <ng-template ngbTabContent><p>Food</p>
    </ng-template>
  </ngb-tab>
</ngb-tabset>

for the above example, even though activeId is wrongly passed (as "not_a_valid_id"), ngb-tabset selected first tab as default. how to achieve this when migrated to ngbNav?

ngbNav did not auto select default tab like ngb-tabset did when a wrong activeId is passed`


Solution

  • Currently, the new nav api doesn't support that feature. It's only set the tab to active if you don't provide any activeId.

    So the only option left is to override the behavior.
    There are several ways to override a directive, each has it own pros and cons, you can find out more here
    Here I take the least required code approach.

    First you need to find out what codeset active to the tab. As so in the link above, it's ngbNav. So the first line of code is create a new directive with the same selector:

    @Directive({
      selector: '[ngbNav]'
    })
    
    export class NewNav {
      constructor(@Host() private originalNav: NgbNav){}
    }
    

    From the source code, you know bootstrap team use ngAfterContentInit, you just need to do the same

    ngAfterContentInit(){
        if(this.originalNav.activeId){
          const isIdExist = this.originalNav.items.filter(item => item.id === this.originalNav.activeId);
          if(!isIdExist.length){
            setTimeout(() => {
              this.originalNav.select(this.originalNav.items.first.id);
            })
          }
        }
      }
    

    The rest of the code should be self-explained, you verify the existence of activeId in the navItem list, if it's not, set it to the first item's id.

    Now the setTimeout is for preventing error from Change Detection, you could use other techniques or even set the activeId value directly.

    The working stackblitz.
    And the idea is taken from the old tabset source code