Generic type 'CanDeactivate<T>' requires 1 type argument(s)

Viewed 4423

In the code below (an example from the book "Angular 2 Development with TypeScript"):

import {CanDeactivate, Router} from "@angular/router";
import {Injectable} from "@angular/core";

@Injectable()
export class UnsavedChangesGuard implements CanDeactivate{

    constructor(private _router:Router){}

    canDeactivate(){
        return window.confirm("You have unsaved changes. Still want to leave?");

    }
}

I see a warning when I hover over CanDeactivate in WebStorm:

enter image description here

Referring to the answers to this question - Generic type 'Observable<T>' requires 1 type argument - the following change removes the warning:

export class UnsavedChangesGuard implements CanDeactivate<any>{

However, I would like to know how I can find out what is the actual argument that CanDeactivate requires.

Edit: Looking at @angular/router/src/interfaces.d.ts we can see the following:

/**
 * @whatItDoes Indicates that a class can implement to be a guard deciding if a route can be
 * deactivated.
 *
 * @howToUse
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canDeactivate(user: UserToken, id: string): boolean {
 *     return true;
 *   }
 * }
 *
 * @Injectable()
 * class CanDeactivateTeam implements CanDeactivate<TeamComponent> {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canDeactivate(
 *     component: TeamComponent,
 *     route: ActivatedRouteSnapshot,
 *     state: RouterStateSnapshot
 *   ): Observable<boolean>|Promise<boolean>|boolean {
 *     return this.permissions.canDeactivate(this.currentUser, route.params.id);
 *   }
 * }
 *
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamCmp,
 *         canDeactivate: [CanDeactivateTeam]
 *       }
 *     ])
 *   ],
 *   providers: [CanDeactivateTeam, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * You can also provide a function with the same signature instead of the class:
 *
 * ```
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamCmp,
 *         canActivate: ['canDeactivateTeam']
 *       }
 *     ])
 *   ],
 *   providers: [
 *     {
 *       provide: 'canDeactivateTeam',
 *       useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => true
 *     }
 *   ]
 * })
 * class AppModule {}
 * ```
 *
 * @stable
 */
export interface CanDeactivate<T> {
    canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean;
}

But it's not clear what "TeamComponent" is.

2 Answers

I hope to be not too late...

Based on an answer of marco-barbero to a question Is there any way to prevent destroying a component in angular?

import { CanDeactivate } from '@angular/router';
import { CanDeactivateComponent } from './app/can-deactivate';

export class ConfirmDeactivateGuard implements CanDeactivate<any> 
{
  canDeactivate(target: any) 
  {
    if(target.hasChanges()){
      return window.confirm('Do you really want to cancel?');
    }
    return true;
  }
}

And in the router entry of the component:

{ 
  path: '',
  component: SomeComponent,
  canDeactivate: [ConfirmDeactivateGuard]
}

Finally, of course, include the guard in a module as a provider:

@NgModule({
  ...
  providers: [
    ...
    ConfirmDeactivateGuard
  ]
})
export class AppModule {

}

Note the use of any as a parameter of CanDeactivate() method in a guard class. Invocation in the route add the correct parameter, so you can use in any Component. The only requiremet is to implement the method hasChanges():boolean (with the appropiate condition in each one) in every Component in which You want to use the guard.

Related