web-dev-qa-db-fra.com

angular2: CanDeactivate guard

J'ai créé un garde CanDeactivate qui renvoie un observable et il est appliqué à un composant qui est chargé dans une sortie de routeur imbriquée intérieure. Faut-il appeler ce garde chaque fois que l'on essaie de naviguer vers une autre URL? Je pose cette question car cela ne se produit pas dans mon cas.

Dans mon cas, le gardien ne sera appelé que pour la première URL "différente". Permettez-moi d'essayer de l'expliquer avec un exemple. Supposons que je retourne toujours false et que j'essaie de naviguer vers différentes URL à partir du même composant:

/A --> guard called
/B --> guard called
/B --> no navigation and no guard called
/A --> guard called
/A -->guard not called and no navigation

Est-ce le comportement attendu?

edit Eh bien, il semble que ce soit le cas. Je viens de construire un petit échantillon avec 3 composants et le gardien ne sera appelé que pour la 1ère fois que l'utilisateur essaie de naviguer vers une URL spécifique ... c'est vraiment bizarre ...

Quoi qu'il en soit, voici le code que j'utilise:

// app.routing
import {NgModule} from "@angular/core";
import {Routes, RouterModule, Route, CanDeactivate, ActivatedRouteSnapshot, 
        RouterStateSnapshot} from "@angular/router";
import { MainComponent } from "./main/main.component";
import { OtherComponent } from "./other/other.component";
import { Other3Component } from "./other3/other3.component";
import {Observable} from "rxjs/observable";
const fallback: Route = {
    path: "**",
    redirectTo: "/main",
    pathMatch: "full"
};
export class Test implements CanDeactivate<MainComponent>{
  canDeactivate(component: MainComponent, route: ActivatedRouteSnapshot, 
            state: RouterStateSnapshot): Observable<boolean> | boolean{
    console.log("in");
    return false;
  }
}
export const rotas: Routes = [
{
    path: "main",
    component: MainComponent,
    canDeactivate: [Test]
},
{
    path: "other",
    component: OtherComponent
},
{
    path: "other3",
    component: Other3Component
},
fallback
];

@NgModule({
 imports: [RouterModule.forRoot(rotas)],
 exports: [RouterModule]
})
export class AppRoutingModule{}

//app.component.html <h1> <a routerLink="/main">Main</a> <a routerLink="/other">Other</a> <a routerLink="/other3">Other3</a> </h1>

Tout a été généré par angular-cli (ex.: N g composant XXX). Oui, la garde CanDeactivate retournera toujours false, vous ne pourrez donc pas décharger le composant principal. Donc, la première fois que je clique sur autre, le garde est appelé. Si vous cliquez à nouveau sur un autre, aucun garde n'est appelé. Cependant, si je clique sur other3, le garde est appelé. Cliquer sur other3 ne fera rien avant d'avoir cliqué sur un autre lien (ex .: autre) ...

Est-ce le comportement attendu? Je dois dire que je m'attendais à ce que ma garde soit frappée à chaque fois que je clique sur un autre lien ...

Merci.

Luis

14
Luis Abreu

Merde, bug ... Note à soi-même: la prochaine fois, vérifiez d'abord le tableau des problèmes

https://github.com/angular/angular/issues/12851#event-880719778

2
Luis Abreu

j'ai trouvé cette solution, au lieu de créer un garde candeactivate pour chaque composant, vous allez créer un service guard et ajouter une méthode candeactivate à chaque composant que vous voulez ajouter cette option, donc vous devez d'abord ajouter ce fichier de service "deactivate-guard .service.ts ":

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';

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

@Injectable()
export class DeactivateGuardService implements  CanDeactivate<CanComponentDeactivate>{

  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

alors vous devez fournir dans le module d'application:

providers: [
    DeactivateGuardService
  ]

maintenant dans le composant que vous souhaitez protéger, ajoutez la fonction:

export class ExampleComponent {
    loading: boolean = false;
    //some behaviour that change the loading value
    canDeactivate() {
        console.log('i am navigating away');
        if (this.loading) {
            console.log('no, you wont navigate anywhere');
            return false;
        }
        console.log('you are going away, goodby');
        return true;
    }
}

vous pouvez voir que le chargement variable est local au composant. la dernière étape consiste à ajouter la directive au composant dans le module de routage:

{ 
  path: 'example', 
  canDeactivate: [DeactivateGuardService],
  component: ExampleComponent 
}

et c'est tout, j'espère que c'était utile, bonne chance.