web-dev-qa-db-ja.com

angular2:CanDeactivateガード

オブザーバブルを返すCanDeactivateガードを作成し、内側のネストされたルーターアウトレットにロードされるコンポーネントに適用します。別のURLに移動しようとするたびに、このガードを呼び出す必要がありますか?私の場合、これは起こっていないので、私はこれを求めています。

私の場合、ガードは最初の「異なる」URLに対してのみ呼び出されます。例で説明してみましょう。私は常にfalseを返し、同じコンポーネントから異なるURLに移動しようとしていると仮定します:

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

これは予想される動作ですか?

編集まあ、それはそうです。 3つのコンポーネントを含む小さなサンプルを作成しましたが、ユーザーが特定のURLに移動しようとすると初めてガードが呼び出されます...これは本当に奇妙です...

とにかく、私が使用しているコードは次のとおりです。

// 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>

すべては、angular-cliを介して生成されました(例:n gコンポーネントXXX)。はい、CanDeactivateガードは常にfalseを返すため、メインコンポーネントをアンロードすることはできません。そのため、初めてotherをクリックすると、ガードが呼び出されます。他をもう一度クリックすると、ガードは呼び出されません。ただし、other3をクリックすると、ガードが呼び出されます。 other3をクリックしても、他のリンク(例:other)をクリックするまでは何もしません...

これは予想される動作ですか?別のリンクにアクセスするたびにガードがヒットすることを期待していたことを言わなければなりません...

ありがとう。

ルイス

14
Luis Abreu

くそー、バグ...自己への注意:次回は、最初に問題ボードを確認してください

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

2
Luis Abreu

私はこのソリューションを見つけました、すべてのコンポーネントにcandeactivateガードを作成する代わりに、1つのガードサービスを作成し、このオプションを追加するすべてのコンポーネントにcandeactivateメソッドを追加するため、最初にこのサービスファイル「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;
  }
}

その後、アプリモジュールで提供する必要があります。

providers: [
    DeactivateGuardService
  ]

保護したいコンポーネントに、関数を追加します:

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;
    }
}

変数のロードはコンポーネントに対してローカルであることがわかります。最後の手順は、ルーティングモジュールのコンポーネントにディレクティブを追加することです。

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

それだけです。これがお役に立てば幸いです。