angulartypescriptangular-materialmat-tab

How to prevent changing MatTab until confirmation?


I want to hold, the changing of MatTab until a confirmation is given. I have used MatDialog for confirmation. The issue is, before clicking "Yes", the the tab is already changed.

For example, From income tab, I click adjustment tab. And before switching to that tab, I need to show the popup first. But I am getting the popup after moving to adjustment tab.

th image which show the popup

component template:

 <mat-tab-group (click)="tabClick($event)">
  <mat-tab *ngFor="let tab of tabs; let index = index" [label]="tab">
    <app-spread></app-spread
  </mat-tab>
</mat-tab-group>

component ts (onClick's method):

tabClick(clickEvent: any) {
    if (clickEvent.target.innerText != 'First') {
      this.confirm();
    }
  }
  public async confirm() {
    const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
      maxHeight: '200px',
      maxWidth: '767px',
      width: '360px',
      disableClose: true,
      data: {
        title: 'Confirmation Message',
        content:
          'There are valid statements that are not Final. Set the statements as Final?'
      }
    });
    const res = dialogRef.afterClosed().subscribe(result => {
      if (result === 1) {
        //TODO need to change the tab
      } else {
        //TODO no need to change the tab
      }
    });
  }

Solution

  • Sometime ago I simulate a "mat-tab" in this SO

    I Imagine that you can use this with only change the function

    <mat-tab-group #tabgroup style="margin-bottom:5px;" 
                   animationDuration="0"   mat-align-tabs="start"
                   (selectedIndexChange)="change(tabgroup,$event)">
    ...
    </mat-tab-group>
    

    And the function change:

      change(tab:any,index:number)
      {
        if (tab.selectedIndex!=this.indexOld){
          const dialogRef = this.dialog.open(....)
          const res = dialogRef.afterClosed().subscribe(result => {
            if (result === 1) {
               this.index=index;
            } else {
               tab.selectedIndex=this.indexOld;
            }
          });
        }
        else
        {
           this.index=index;
        }
      }
    

    See the stackblitz -in the stackblitz I simple use the confirm dialog-

    Update problem if we use a mat-dialog.

    There's another approach that it's overwrite the behaviour of the mat-tab-group

    We need change the _handleClick function of the mat-tab-group, (see this function in github) and the function _handleKeydown of the mat-header (see this function in github

    It's a bit complex, but the only is get the mat-group using ViewChild (I use {static:true} because my tabgroup is visible always.

    @ViewChild('tabgroup', { static: true }) tabgroup: MatTabGroup;
    

    Then, in ngOnInit (if it's not static:true, we use ngAfterViewInit), simply redefine the function. for this first "cast" to any this.tabgroup

    First we makes a function alertDialog

      alertDialog(index:number,tab:MatTab,tabHeader:any,)
      {
        const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
          data: {},
        });
        const res = dialogRef.afterClosed().subscribe((result) => {
          if (result) {
            tabHeader.focusIndex = index;
    
            if (!tab.disabled) {
              this.tabgroup.selectedIndex = index;
            }
          }
        });
    
      }
    

    Then, in ngOnInit we write

    ngOnInit(){
        (this.tabgroup as any)._handleClick = (
          tab: MatTab,
          tabHeader: any,
          index: number
        ) => {
          if (this.tabgroup.selectedIndex != index)
            this.alertDialog(index, tab, tabHeader);
        };
    }
    

    To override the _handleKeydown we are enclosed all in setTimeout to give time to Angular to paint the mat-tab-header

      ngOnInit() {
        ....
        setTimeout(() => {
          const tabHeader = (this.tabgroup as any)._tabHeader;
          tabHeader._handleKeydown = (event: KeyboardEvent) => {
            if (hasModifierKey(event)) {
              return;
            }
    
            switch (event.keyCode) {
              case ENTER:
              case SPACE:
                if (tabHeader.focusIndex !== tabHeader.selectedIndex) {
                  const item = tabHeader._items.get(tabHeader.focusIndex);
                  this.alertDialog(tabHeader.focusIndex, item, tabHeader);
                }
                break;
              default:
                tabHeader._keyManager.onKeydown(event);
            }
          };
        });
    

    You can see in a new stackblit