Chris
Chris

Reputation: 1558

get current route component instance or guards

I have the navigation area including a logout button next to my router-outlet.

app.component.html:

<my-nav-header></my-nav-header>
<router-outlet></router-outlet>

On every module/component in the router I have implemented a canDeactivate method for dirty-checking (as described in the Routing & Naviagtion tutorial of angular) that is called from the actual canDeactivateGuard via an interface as described in that tutorial. This works as expected - when a user clicks a router link and has unsaved changes he is asked whether to route anyway or not.

my-nav-header.component.ts

logoutBtnClick(){
    // todo: insert call to canDeactivate here
    // or find another way to get dirty state of current component in router
    //console.debug('logout - can deactivate[1]: ', this.activatedRoute.routeConfig.canDeactivate);
    //console.debug('logout - can deactivate[2]: ', this.activatedRoute.snapshot.routeConfig.canDeactivate);
    this.loginSvc.doLogout();
}

login.service.ts

@Injectable()
export class LoginService {

...

  public doLogout(){
    this.myHttp.sendLogout().subscribe(
        ()=> {
            this.clearSessionData();
            this.myHttp.clearSessionData();
            this.router.navigate(['/login']); // triggering canDeactivate, but too late as logout command already has been sent to the server
  }

When the user presses the logout button I want the dirty check to be executed (giving the user the possibility to cancel logout) before my logout method actually sends the logout command to the server.

I have tried to inject both Router and ActivatedRoute into my navigation header component, but in both cases the rootConfig property is null preventing me from accessing canDeactivate.

So my question is: How to access either the canDeactivate guard of the current route or the instance of the current route component itself?

Upvotes: 2

Views: 2475

Answers (2)

Chris
Chris

Reputation: 1558

For anyone having similar problems: Although it is not a real solution for the my original question (get instance of current route or access their guards manualla), I could acieve my goal by changing the order and making use of the promise returned by router.navigate(...):

@Injectable()
export class LoginService {

...

  public doLogout(){
    this.router.navigate(['/login'])
      .then((routerNavigationResult: boolean)=> {
        if (routerNavigationResult) {
          // only when the navigation is allowed, i.e. no change or change disccarded
          this.myHttp.sendLogout().subscribe(
            (data) => this.clearSessionData(),
            (error) => this.handleLogoutError(error) // in my case also clears session data
          );
        }
      });
  }

Upvotes: 1

Teddy Sterne
Teddy Sterne

Reputation: 14221

The CanDeactivate class takes the type of the component that it is checking on. By defining an interface for a component that has a canDeactivate method you can check if the method exists and conditionally call it on the active component if it implements the method. Otherwise you can return true and allow the component to be deactivated;

interface ICanComponentDeactivate extends Component {
    canDeactivate: () => Observable<boolean> | boolean;
}

export class CanDeactivateGuard implements CanDeactivate<ICanComponentDeactivate> {
    public canDeactivate(currentComponent: ICanComponentDeactivate,
                         route: ActivatedRouteSnapshot,
                         state: RouterStateSnapshot): Observable<boolean> | boolean {
        return currentComponent.canDeactivate ? currentComponent.canDeactivate() : true;
    }
}

Upvotes: 1

Related Questions