我的应用程序使用由服务管理的延迟加载选项卡系统。 当用户在导航菜单上选择一个选项时,会发生两件事:
这很好用,但我需要更复杂的东西:
RouteReuseStrategy
最适合这种行为。目前,我找到的唯一解决方案是将每个组件的状态存储在会话存储中,并在单击选项卡时更新组件状态。为此,我的组件需要订阅
NavigationStart
和 NavigationEnd
事件。
这适用于相当简单的组件,但适用于一些包含数十个按钮、表单等的非常复杂的组件。它很快就会成为管理的噩梦,因为我必须为每个组件编写加载和保存逻辑。会话存储的大小有限,这也是这种方法的限制。
非常感谢任何建议。
您可以使用
ngOnInit
和 ngOnDestroy
生命周期挂钩从某些持久存储中加载/存储状态。您建议使用 local-storage,这是一个不错的选择,特别是如果您还希望在浏览器中重新加载/刷新或用户稍后重新访问页面时恢复状态。否则会话存储也是一个选项(阅读 MDN 上的差异这里)。如果不希望在页面重新加载时恢复,您也可以简单地将状态存储在某些服务中(存储在浏览器内存中而不是 Web 存储中)。
阅读更多关于 Angular 生命周期钩子的信息在官方 Angular 文档.
正如您在评论中提到的,如果您路由到完全相同的组件而不破坏它,这将不起作用。
在这种情况下,您可以使用路由解析器:
假设您的应用程序或模块中有一个
DataService
实例,您要使用它来提供数据。
为您的
TabComponent
配置路线,例如:
{
path: '',
component: TabPageComponent,
runGuardsAndResolvers: 'always', // Set flag to run always
resolve: {
// Provide the data service, assuming it is available in the injector
data: DataService,
},
}
现在你的
DataService
应该实现 Resolve
的 '@angular/router'
接口:
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
export class DataService implements Resolve<any> {
// constructor with dependencies should be implemented as needed for your case.
public resolve(route?: ActivatedRouteSnapshot, state?: RouterStateSnapshot): Observable<any> |
// Do your thing here using route and state.
// i.e. API call, load from Web Storage or simply from memory in this service.
}
}
现在,每次您的路由更改时,路由器都会在您的服务中调用 resolve 。在您的组件中,您可以订阅激活的路由数据,这是一个相应发出的可观察对象:
import { ActivatedRoute } from '@angular/router';
//...
class TabComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
public ngOnInit(): void {
this.route.data.subscribe((data) => {
const data = data.data;
// Do something with your data.
}
}
}
替代方法是创建一个可观察对象并直接在您的视图中使用它:
import { ActivatedRoute } from '@angular/router';
//...
class TabComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
public data$ = Observable<any>;
public ngOnInit(): void {
this.data$ = this.route.data.pipe(
map(data => data.data),
);
}
}
根据您的需要,您可以通过使用其他选项之一配置它来更改
runGuardsAndResolvers
以降低运行频率。有关可能的选项,请参阅Angular 文档。
对于遇到相同问题的任何人,可以通过实施
RouteReuseStrategy
来实现粘性状态。你可以在这里找到一个例子.
我认为这种方法不适用于同一组件的多个实例,但它确实适用。你只需要稍微调整一下实现:
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
,这样即使路由没有改变也会加载新组件。缓存路由重用策略
@Injectable()
export class CacheRouteReuseStrategy implements RouteReuseStrategy {
constructor(private linkService : LinkService) {
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
// If previous route and active route are the same, handle is null.
// In that case, we don't want to update the store (we don't want to set a null object for the route and loose informations)
if (handle != null && this.linkService.previousLink != null){
this.linkService.storedRoutes.set(route.routeConfig?.path + this.linkService.previousLink.tabId, handle);
}
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (this.linkService.activeLink != null) {
return !!route.routeConfig && !!this.linkService.storedRoutes.get(<string>route.routeConfig.path + this.linkService.activeLink.tabId);
} else {
return false;
}
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
// @ts-ignore
return this.linkService.storedRoutes.get(route.routeConfig.path + this.linkService.activeLink.tabId);
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}
我有一个名为 LinkService 的服务来跟踪我要离开的路线 (previousLink) 和我试图访问的路线 (activeLink)。 previousLink 是我想在离开路线时添加到商店的对象,而 activeLink 是我想在访问路线时从商店获取的对象。
@Injectable({
providedIn: 'root'
})
export class LinkService {
links: LinkModel[];
activeLink: LinkModel;
previousLink: LinkModel;
storedRoutes = new Map<string, DetachedRouteHandle>();
}
export interface LinkModel {
route: string,
parameters: string | null,
tabId: string,
tabTitle: string
}
这些信息由另一个名为 TabService 的服务更新。它的作用非常简单(创建、更改和删除选项卡逻辑),但如果有人需要,我可以稍后发布。