问题
I'm trying to use the canDeactivate router guard. The component I'm passing in (ClaimsViewComponent) is null the first time the code runs. But on subsequent runs, the code runs as expected. We're trying to figure out why the component is null on first run.
This is the canDeactivate guard code:
@Injectable()
export class ConfirmDeactivateGuard implements
CanDeactivate<ClaimsViewComponent> {
canDeactivate(target: ClaimsViewComponent): boolean {
if (target.canDeactivate()) {
return window.confirm('Do you really want to cancel?');
}
return true;
}
}
This is the routing module code:
const routes: Routes = [
{ path: ':type', component: ClaimsViewComponent, canDeactivate:
[ConfirmDeactivateGuard] }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
Here is the full error stack:
TypeError: Cannot read property 'canDeactivate' of null
at ConfirmDeactivateGuard.canDeactivate (confirm-deactivate-guard.ts:16)
at MergeMapSubscriber.eval [as project] (router.js:3933)
at MergeMapSubscriber._tryNext (mergeMap.js:128)
at MergeMapSubscriber._next (mergeMap.js:118)
at MergeMapSubscriber.Subscriber.next (Subscriber.js:95)
at ArrayObservable._subscribe (ArrayObservable.js:116)
at ArrayObservable.Observable._trySubscribe (Observable.js:172)
at ArrayObservable.Observable.subscribe (Observable.js:160)
at MergeMapOperator.call (mergeMap.js:92)
at Observable.subscribe (Observable.js:157)
at ConfirmDeactivateGuard.canDeactivate (confirm-deactivate-guard.ts:16)
at MergeMapSubscriber.eval [as project] (router.js:3933)
at MergeMapSubscriber._tryNext (mergeMap.js:128)
at MergeMapSubscriber._next (mergeMap.js:118)
at MergeMapSubscriber.Subscriber.next (Subscriber.js:95)
at ArrayObservable._subscribe (ArrayObservable.js:116)
at ArrayObservable.Observable._trySubscribe (Observable.js:172)
at ArrayObservable.Observable.subscribe (Observable.js:160)
at MergeMapOperator.call (mergeMap.js:92)
at Observable.subscribe (Observable.js:157)
at resolvePromise (zone.js:814)
at resolvePromise (zone.js:771)
at eval (zone.js:873)
at ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:4740)
at ZoneDelegate.invokeTask (zone.js:420)
at Zone.runTask (zone.js:188)
at drainMicroTaskQueue (zone.js:595)
Thank you.
回答1:
I fixed this issue by adding the canDeactivate inside my page.module.ts instead of app.module.ts
回答2:
It seems, canDeactivate
not working on 'lazy loading'. Please check your route, and make sure, that you have set the component
property.
Update with @rohan-sampat feedback.
It works. I too had issue with CanDeactivate in my project.
CanDeactivate-guard.service.ts
import { Observable } from 'rxjs';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
export interface CanComponentDeactivate {
canComDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return component.canComDeactivate();
}
}
This is the component where I was using CanDeactivate to validate.
import { Component, EventEmitter, Output, OnInit } from "@angular/core";
import { DataService } from '../Shared/Service/DataService';
import { ActivatedRoute } from '@angular/router';
import { Subscription, Observable } from 'rxjs';
import { CanComponentDeactivate } from '../can-deactivate-guard.service';
@Component({
selector: 'app-server',
templateUrl: './server.component.html'
})
export class ServerComponent implements OnInit, CanComponentDeactivate {
@Output() serverCreated = new EventEmitter<string>();
queryParams: Subscription;
data: string;
canMove: false;
userModel: any;
constructor(private dataService: DataService, private route: ActivatedRoute) {
}
ngOnInit() {
this.dataService.dataUpdate.subscribe(data => {
this.data = data;
})
this.queryParams = this.route.queryParams.subscribe(data => {
this.userModel = JSON.parse(data["model"]);
})
}
canComDeactivate(): Observable<boolean> | Promise<boolean> | boolean {
if (this.data == undefined || this.data == "") {
var res = confirm("Text is blank");
return res;
}
return this.canMove;
}
}
The app.module.ts file in which I had my routes defined.
const appRoutes: Routes = [
// { path: '', loadChildren: () => import('./Login/login.module').then(l => l.LoginModule) },
{ path: 'login', loadChildren: () => import('./Login/login.module').then(l => l.LoginModule) },
{ path: 'server', canActivate: [AuthGuard], canDeactivate: [CanDeactivateGuard], loadChildren: () => import('./severs/server.module').then(s => s.ServerModule) },
{ path: '**', component: ErrorComponent },
];
CanDeactivate on path Server was causing issue. If I was trying to go to another path outside the module, then id does not even hit the CanDeactivate guard.
But if I placed CanDeactivate from app.module to server.routes files, means in lazy loaded module itself to validate it, then it was working properly, as expected.
Server.route.ts file
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ServerComponent } from './server.component';
import { ErrorComponent } from './404.component';
import { CanDeactivateGuard } from '../can-deactivate-guard.service';
const route: Routes = [
{ path: '', component: ServerComponent },
{ path: 'server:/model', component: ServerComponent, canDeactivate: [CanDeactivateGuard] },
{ path: 'server', component: ServerComponent, canDeactivate: [CanDeactivateGuard] },
{ path: 'not-found', component: ServerComponent },
//{ path: '**', component: ErrorComponent },
]
@NgModule({
imports: [RouterModule.forChild(route)],
exports: [RouterModule]
})
export class ServerRoute { }
Now after this change in solution, my code works was expected. Hope this helps.
来源:https://stackoverflow.com/questions/51450323/typeerror-cannot-read-property-candeactivate-of-null