I am loading my routes lazily. I am trying to use CanDeactivate
as per the documentation but it seems that the guard can not read the component's properties or methods.
This is my guard,
@Injectable()
export class CanDeactivateGuard implements CanDeactivate<PatientRegistrationFormComponent> {
constructor() {}
canDeactivate(component: PatientRegistrationFormComponent) {
return component.isChangesSaved();
}
}
isChangesSaved()
is a method inside the component.
public isChangesSaved(): Observable<boolean> {
if (this.isDirtyForm()) {
return this.confirmService.confirmUnsavedChanges();
}
return of(false);
}
This is the error,
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'isChangesSaved' of null TypeError: Cannot read property 'isChangesSaved' of null at CanDeactivateGuard.push../src/app/core/database/database-cores/registry-core/guards/patient-registration.guard.ts.CanDeactivateGuard.canDeactivate
Here is my module (lazy loaded)
@NgModule({
imports: [
CommonModule,
RegistryRoutingModule,
ReactiveFormsModule,
FormsModule,
],
declarations: [
RegistryContainerComponent,
RegistryHomeComponent,
PatientRegistrationFormComponent, // component of interest ….
],
exports: [PatientRegistrationFormComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [
…fromGaurds.guards, // all my guards are loaded here….
{ provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher },
ToastService,
],
})
export class RegistryModule {}
and this is my routing module
const routes: Routes = [
{
path: '',
component: RegistryContainerComponent,
children: [
{
path: '',
component: RegistryHomeComponent,
},
{
path: 'patientregistration',
component: PatientRegistrationFormComponent,
canDeactivate: [fromGaurds.CanDeactivateGuard],
children: [
{
path: '',
component: PatientRegistrationFormComponent,
canActivate: [fromGaurds.PatientRegistryGuard],
canDeactivate: [fromGaurds.CanDeactivateGuard],
},
{
path: ':id',
component: PatientRegistrationFormComponent,
canActivate: [fromGaurds.PatientRegistryRecordGuard],
canDeactivate: [fromGaurds.CanDeactivateGuard],
},
],
},
],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class RegistryRoutingModule {}
is this a bug or wrong implementation of the interface?
Update
I tired adding the guard to the lazy loading module.
This is the parent module for the child module RegistryModule
mentioned above,
const dbRoutes: Routes = [
{
path: '',
component: DbHomeComponent,
data: { preload: true },
canDeactivate: [fromGaurds.CanDeactivateGuard],
children: [
{ path: '', component: UsersDashboardComponent },
{
path: 'registry',
canActivate: [RoleGuardService],
data: { expectedRole: { roles: ['reg'] } },
loadChildren: '../database-cores/registry-core/modules/registry.module#RegistryModule',
},
{
path: 'cloudstorage',
canActivate: [RoleGuardService],
loadChildren:
'../database-cores/cloud-storage-core/modules/cloud-storage.module#CloudStorageModule',
},
],
},
];
@NgModule({
imports: [RouterModule.forChild(dbRoutes)],
exports: [RouterModule],
})
export class DbRoutingModule {}
@NgModule({
declarations: [
DbHeaderComponent,
DbFooterComponent,
DbHomeComponent,
UsersDashboardComponent,
],
imports: [
}),
],
exports: [],
providers: [
...fromGaurds.guards,
MDBSpinningPreloader,
ToastService,
DirectoryService,
],
schemas: [NO_ERRORS_SCHEMA],
})
export class DbModule {}
I modified your stackblitz to reproduce the issue. The core of the issue is that the same deactivate guard for the same component is firing twice. The second time it fires, the component has already been deactivated and no longer exists, and hence the error.
The offending code is in the RegistryRoutingModule. The parent route 'patientregistration'
and child route ''
are both active at the same time, and use the same guard. If you remove one of the guards it should work. Depending on your use case you could remove the guards from the parent or child, however given the guard is going to cascade, you could remove from all the children.
const routes: Routes = [
{
path: '',
component: RegistryContainerComponent,
children: [
{
path: '',
component: RegistryHomeComponent,
},
{
path: 'patientregistration',
component: PatientRegistrationFormComponent,
canDeactivate: [fromGaurds.CanDeactivateGuard],
children: [
{
path: '',
canActivate: [fromGaurds.PatientRegistryGuard]
},
{
path: ':id',
canActivate: [fromGaurds.PatientRegistryRecordGuard]
},
],
},
],
},
];