我喜欢resolver
s。
你可以这样说:
- 对于给定的路线,您希望首先加载一些数据
- 你可以拥有一个没有可观察性的非常简单的组件(从this.route.snapshot.data
检索数据)
所以解析器很有意义。
BUT:
- 在收到实际响应之前,您不会更改URL并显示所请求的组件。因此,您不能(简单地)通过呈现组件并尽可能多地显示用户来显示用户正在发生的事情(就像建议使用PWA的shell应用程序一样)。这意味着当连接不良时,您的用户可能只需等待很长时间没有视觉指示发生了什么。
- 如果你在使用param的路线上使用解析器,让我们以users/1
为例,它将在第一次正常工作。但是如果你去users/2
,除非你开始使用另一个observable,否则什么都不会发生:this.route.data.subscribe()
所以感觉解析器可能有助于检索一些数据但在实践中我不会使用它们以防网络速度慢,特别是对于带有参数的路由。
我在这里遗漏了什么吗?有没有办法将它们与那些真正的约束一起使用?
答案 0 :(得分:4)
解析器:它甚至在用户被路由到新页面之前就被执行。
每当需要在组件初始化之前获取数据时,执行此操作的正确方法是使用解析器。解析器同步进行操作,即解析器将等待异步调用完成,并且只有在处理了异步调用之后,它才会路由到相应的URL。因此,组件初始化将等待直到回调完成。因此,如果您想做一些事情(服务呼叫),即使在组件初始化之前,您就来对地方了。
示例场景: 我正在研究一个项目,用户将在该项目中传递要在URL中加载的文件名。根据传递的名称,我们将在ngOnInit中进行异步调用并获取文件。但这是一个问题,如果用户在URL中传递了不正确的名称,我们的服务将尝试获取服务器上不存在的文件。在这种情况下,我们有2种选择:
选项1:在ngOnInit中获取有效文件名列表,然后调用实际服务以获取文件(如果文件名有效)。 这两个呼叫应该是同步的 。
选项2:在解析器中获取有效文件名列表,检查URL中的文件名是否有效,然后获取文件数据。
选项2是更好的选择,因为解析器可以处理呼叫的同步性。
重要信息::甚至在将用户路由到URL之前,要获取数据时,请使用解析器。解析程序可能包含服务调用,这将为我们带来加载下一页所需的数据。
答案 1 :(得分:2)
我一直在想完全相同的事情。
我在此遇到的关于该主题的最直观的讨论是:https://angular.schule/blog/2019-07-resolvers。
作者基本上可以归结为这一点:如果您已经使用了解析器,并且没有任何UX问题,那就去解决。但是在大多数情况下,解析程序会增加不必要的复杂性,您最好采用使用“智能容器”和“表示组件”结构的被动方法。很少有例外。
使用这种结构,智能组件可以作为更动态的解析器形式,而您的演示组件可以处理伪同步数据的显示。
据我所知,对于那些不习惯使用反应式模式的人来说,解析器本质上是一个拐杖。
答案 2 :(得分:1)
解析器为您提供了路由器导航开始时的一个钩子,它使您可以控制导航尝试。这给了您很多自由,但没有很多硬性规定。
您不必在组件中使用解析结果。您可以仅将解决方法用作挂钩。由于您引用的原因,这是我首选的使用方式。在组件中使用结果要简单得多,但是需要进行同步权衡。
例如,如果您使用的是Material或Cdk之类的东西,则可以分派“正在加载”对话框以在解决方案的开始处显示进度指示器,然后在解决方案结束时关闭该对话框。像这样:
constructor(route: ActivatedRouteSnapshot, myService: MyService, dialog: MatDialog) {}
resolve() {
const dialogRef = this.dialog.open(ProgressComponent);
return this.myService.getMyImportantData().pipe(
tap(data => this.myService.storeData(data)),
tap(() => dialogRef.close()),
map(() => true),
catchError(err => of(false))
);
}
如果您使用的是ngrx
,则可以在解决过程中通过分派操作来做类似的事情。
当您以这种方式使用Resolve保护器时,没有特别强烈的理由使用Resolve保护器而不是CanActivate保护器。这种选择取决于语义。我发现在CanActivate上进行身份验证并从Resolve启动数据检索更加明显。当允许这些主题融合在一起时,这将是一个明智的选择。
答案 3 :(得分:1)
这就是为什么我要使用解析器的原因:
myapp.com/lists
的“搜索结果”页面,导航到列表myapp.com/lists/1
的一个元素,显示该元素的详细信息,无需获取数据通过搜索完成。然后,假设您直接导航到需要获取的myapp.com/lists/1
,然后导航到组件将解析器作为应用程序和组件之间的中间件,您可以在父组件(其中包含<router-outlet>
)中管理加载视图。
答案 4 :(得分:0)
我目前实际上正在重构一个使用很多解析器的应用程序,对此进行了很多思考,并认为它们的最大问题是它们会突变数据,您必须映射从activatedRoute获取的数据。换句话说,这增加了维护应用程序的复杂性和问题,而直接服务注入则没有此问题。...此外,由于解析程序是同步的,因此在大多数情况下,这实际上会减慢用户体验...
答案 5 :(得分:0)
在我看来,如果您不介意在加载数据时无法立即将用户导航到新部分,您可以在组件代码中使用它们以简化和清晰。
为了改善用户体验,让用户知道正在加载某些内容,您可以创建一个服务来保持加载状态。它将订阅路由器事件并根据发生的情况将变量设置为不同的值。例如,我已经习惯了 3 个基本状态:initial_load
,应用程序第一次运行时,resolving
,解析时,以及 done
。
每当开始导航或首次加载应用时,包含 <router-outlet>
标记的组件将改为显示加载器,直到加载状态设置为完成。
对于你的第二个问题,关于从 users/1 到 users/2 的导航,你是完全正确的。要解决此问题,您必须订阅用户详细信息组件中的路由参数更改。这通常不会发生在我使用过的应用中,它们有一个表格,您可以在其中选择一个用户,而要选择另一个用户,您必须导航回列表。