我阅读了有关使用CanDeactivate来检测用户是否离开表单/路线的信息,这是我在项目中所需要的。我尝试实施此SO答案:SO Answer
当我查看用户窗体组件时,我将信息输入其中一个输入中,然后尝试刷新,单击另一个组件,然后通过浏览器URL导航至google.com。这些都不会触发该答案中的确认提示。
我的项目结构是这样设置的:
> src
> app
- app-routing.module.ts
- app.module.ts
- app.component.ts
- app.component.html
> auth
> ...
> dashboard
- dashboard.module.ts
- dashboard.component.ts
- dashboard.component.html
> components
> forms
- user-form.component.ts
- user-form.component.html
> ...
> shared
> guards
- pending-changes.guard.ts
> ...
我已经尝试实现此SO答案:SO Answer
在我的仪表板模块中,导入了我的pending-changes.guard,并为使用我的canDeactivate的仪表板组件定义了一条路由:[PendingChangesGuard] 然后,在我的仪表板模块的NgModule中将PendingChangesGuard添加为提供程序。
在用户窗体组件中,我从共享的PendingChangesGuard中导入ComponentCanDeactivate,然后在组件中实现ComponentCanDeactivate接口。我定义了一个名为canDeactivate()的函数,该函数检查FormGroup类型的userForm是否脏,如果返回脏,则返回false,该错误应向用户显示确认信息。如果尚未使用该表单,那么我想返回true。
在我的仪表板模块中定义的路线是针对我的仪表板组件的。我的仪表板组件是我的用户窗体组件的另一个组件的父级。因此,当用户通过单击其他组件来显示该组件时,包含我的表单html的用户窗体组件没有专门设置自己的url / path / route / routerLink。我不确定这里的设置是否正确,因为我看到的大多数示例都有专门为表单组件设置的route / path / routerLink,其中包含未保存的数据会触发用户提示。
pending-changes.guard.ts
import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
export interface ComponentCanDeactivate {
canDeactivate: () => boolean | Observable<boolean>;
}
@Injectable()
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
// if there are no pending changes, just allow deactivation; else confirm first
return component.canDeactivate() ?
true :
// NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
// when navigating away from your angular app, the browser will show a generic warning message
// see http://stackoverflow.com/a/42207299/7307355
confirm('WARNING: You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.');
}
}
dashboard.module.ts
import { PendingChangesGuard } from '../shared/guards/pending-changes.guard';
export const routes: Routes = [
{
path: '',
component: DashboardComponent,
canDeactivate: [PendingChangesGuard]
}
];
@NgModule({
imports: [
CodemirrorModule,
CommonModule,
CronEditorModule,
FormsModule,
ReactiveFormsModule,
SimpleNotificationsModule.forRoot(),
NgbModule,
NgxsModule.forFeature(STATES),
NgxSpinnerModule,
RouterModule.forChild(routes),
SharedModule
],
declarations: COMPONENTS,
exports: COMPONENTS,
entryComponents: [ProfileComponent],
providers: [PendingChangesGuard]
})
export class DashboardModule {}
user-form.component.ts
import { ComponentCanDeactivate } from '../../../../shared/guards/pending-changes.guard';
@Component({
selector: 'user-form',
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.scss']
})
export class UserFormComponent implements OnInit, ComponentCanDeactivate {
...
userForm: FormGroup;
constructor(private fb: FormBuilder) {}
// @HostListener allows us to also guard against browser refresh, close, etc.
@HostListener('window:beforeunload')
canDeactivate(): Observable<boolean> | boolean {
// insert logic to check if there are pending changes here;
if (this.userForm.dirty === true) return false;
return true;
// returning true will navigate without confirmation
// returning false will show a confirm dialog before navigating away
}
...
}
当我查看用户窗体组件时,我将信息输入其中一个输入中,然后尝试刷新,单击另一个组件,然后通过浏览器URL导航至google.com。这些都不会导致我的PendingChangesGuard中出现确认提示。我希望用户在用户表单组件中有未保存的数据时得到提示。
更新: 我弹出确认以刷新和浏览器URL导航,但这不能满足我的所有需求,我认为此功能主要来自@HostListener。不过,这似乎并不是我以为我要发送给Guard的确切消息。我在某处看到浏览器自行处理这些类型的确认,所以我并不感到惊讶,但这也许意味着我应该使用自己的自定义模式来处理此确认吗?我正在使用Chrome版本70.0.3538.77(正式版本)(64位)来测试我的用户界面。
我正在考虑的其他一些问题是,我的这个大组件由其他组件组成。问题是我和原始项目负责人(我是该项目的新手,现在他不再在公司工作了,现在我仍然任职于角子公司)从未想到要通过仪表板组件路由组件,因为我们处理了要显示的内容在每个组件中通过NGXS状态管理。通常,顶层组件(仪表板导航选项卡)具有@inputs和@outputs,以允许数据从状态向上/向下流过各个组件,而该状态是从NGXS操作获取状态的,这些操作进行API调用并将数据放置在状态模型,以便在仪表板组件中进行显示。
并且因为我没有每个组件的路由,所以如果用户单击某个按钮使另一个组件通过ngSwitch而不是我的用户窗体组件显示,那么我不会收到未保存数据的确认我的用户表单组件,因为从技术上讲我不是在“导航”或更改路线。
那么我应该如何组织这个项目?即使我的仪表板模块具有来自app-routing.module.ts的延迟加载子级,我是否也将路由添加到仪表板模块路由中的每个组件? 还是在检查表单时是否必须在所有可单击的对象中构建钩子以检查表单的脏值? 还是有一种更简单的方法来从用户窗体组件处理此问题,例如使用生命周期挂钩(我想到了OnDestroy(),但我认为在生命周期中处理该问题为时已晚)?
app-routing.module.ts具有这些路由
const routes: Routes = [
{
path: 'dashboard',
canActivate: [AuthGuard],
loadChildren: './dashboard/dashboard.module#DashboardModule'
},
{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full'
}
];