5

I have an angular component which uses an angular material tab group.

<mat-tab-group>
  <mat-tab label="First"> <app-comp1></app-comp1> </mat-tab>
  <mat-tab label="Second"> <app-comp2></app-comp2> </mat-tab>
  <mat-tab label="Third"> <app-comp3></app-comp3> </mat-tab>
</mat-tab-group>

In a certain tab, user can do some changes and save. If user did some changes and tries to navigate to another tab without saving, I want to ask the user for confirmation to discard changes before navigating to the other tab.

Is there any way to do this?

Lahiru Chandima
  • 22,324
  • 22
  • 103
  • 179
  • 1
    if you are using routing you can use `CanDeactivate` router guard. – prady Jul 16 '18 at 03:37
  • 1
    https://github.com/angular/material2/issues/2013 – yurzui Jul 16 '18 at 03:38
  • Let's say user did some changes in tab one and then tries to navigate to tab two, you can bind the second and third tab label with a (click) event which check if user has done some work or not, if yes then show the pop up which either saves the data(either save directly or route the user back to tab one) or discard, if discard then remove the user's activities so on clicking any other tab it does not show the pop up. – Immad Hamid Jul 16 '18 at 03:56
  • https://stackoverflow.com/questions/56607394/angular-material-tab-prevent-tab-change-of-mat-tab-group-if-the-form-in-curren – jophab May 22 '20 at 15:08

2 Answers2

24

If there is no solution today then i can offer you some trick based on monkey patching:

template.html

<mat-tab-group #tabs>
  ...
</mat-tab-group> 

component.ts

import { MatTabGroup, MatTabHeader, MatTab } from '@angular/material';
...
@Component({...})
export class AppComponent implement OnInit {
  @ViewChild('tabs') tabs: MatTabGroup;

  ngOnInit() {
    this.tabs._handleClick = this.interceptTabChange.bind(this);
  }

  interceptTabChange(tab: MatTab, tabHeader: MatTabHeader, idx: number) {
    const result = confirm(`Do you really want to leave the tab ${idx}?`);

    return result && MatTabGroup.prototype._handleClick.apply(this.tabs, arguments);
  }
}

Ng-run Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • This works perfectly, but can you explain a bit more how this works? is this part of Angular / Material / Html / Javascript? – Rabbi Shuki Gur Jan 21 '19 at 09:01
  • This does work to prevent tab change, but the tab information it receives is for the NEW tab requested, not the previously active tab. So in my case this is no better than the built-in tab changed event. – GeekyMonkey Apr 08 '19 at 12:53
  • @GeekyMonkey You can get that previously active tab through `this.tabs.selectedIndex` – yurzui Apr 09 '19 at 11:02
  • Thank you @yurzui. This is genius and exactly what I needed. One thing to note that might help someone else is for `arguments`, make sure to pass along the tab, tabHeader and idx where @yurizui has noted arguments. `apply(this.tabs, [tab, tabHeader, idx])` – jellybeans May 05 '20 at 22:39
  • Wasn't working for me until I used `ngAfterViewInit` rather than ngOnInit – Dodi Jun 25 '20 at 13:43
  • 3
    @Dodi Consider adding `static: true` to `@ViewChild` decorator if you're on Angular 9 and above – yurzui Jun 25 '20 at 13:45
  • 1
    This unfortunately will not work in case of using keyboard to focus and change tab – Tomasz Oct 05 '21 at 13:23
1

The accepted answer has stopped working for me in the latest vue version.

Another option is to set the other tabs to disabled when the active tab is in a state that is incomplete.

<mat-tab-group>
  <mat-tab label="Form">Form Here</mat-tab>
  <mat-tab label="Result" >Result Here</mat-tab>
</mat-tab-group>
GeekyMonkey
  • 12,478
  • 6
  • 33
  • 39