Búsqueda de sitios web

angular - El tipo genérico 'CanDeactivate' requiere 1 argumento(s) de tipo


En el código a continuación (un ejemplo del libro "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?");

    }
}

Veo una advertencia cuando paso el cursor sobre CanDeactivate en WebStorm:

Refiriéndose a las respuestas a esta pregunta - El tipo genérico 'Observable' requiere 1 argumento de tipo - el siguiente cambio elimina la advertencia:

export class UnsavedChangesGuard implements CanDeactivate<any>{

Sin embargo, me gustaría saber cómo puedo averiguar cuál es el argumento real que requiere CanDeactivate.

Editar: Al mirar @angular/router/src/interfaces.d.ts podemos ver lo siguiente:

/**
 * @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;
}

Pero no está claro qué es "TeamComponent".

Respuesta #1

La documentación de la API proporciona enlaces al código fuente al final de la página.

La interfaz CanDeactivate utiliza genéricos para definir el tipo del primer argumento canDeactivate (componente desactivado):

export interface CanDeactivate<T> {
  canDeactivate(
      component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot,
      nextState?: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean;
}

Como se muestra en esta guía, canDeactivate se llama con la instancia del componente desactivado como argumento:

export interface CanComponentDeactivate {
 canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

Donde component.canDeactivate es solo un método con el mismo nombre en la clase del componente, no necesariamente tiene que llamarse canDeactivate o incluso existir. Y CanComponentDeactivate es solo una interfaz definida por el usuario que proporciona una convención para el método canDeactivate del componente.

Esto permite interactuar con el componente durante la desactivación,

El método canDeactivate() te proporciona la instancia actual del componente, la ActivatedRoute actual y el RouterStateSnapshot en caso de que necesites acceder a alguna información externa. Esto sería útil si solo quisieras usar esta guarda para este componente y necesitaras obtener las propiedades del componente o confirmar si el enrutador debería permitir la navegación fuera de él.

Respuesta #2

Espero no llegar demasiado tarde...

Basado en una respuesta de marco-barbero a una pregunta: ¿Hay alguna manera de evitar destruir un componente en 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;
  }
}

Y en la entrada del enrutador del componente:

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

Finalmente, por supuesto, incluya al guardia en un módulo como proveedor:

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

}

Observe el uso de any como parámetro del método CanDeactivate() en una clase guard. La invocación en la ruta añade el parámetro correcto, por lo que puede usarse en cualquier Componente. El único requisito es implementar el método hasChanges():boolean (con la condición apropiada en cada uno) en cada Componente en el que desee usar el guard.

Artículos relacionados