如何在 Durandal 中显示带有参数化路由的 "not found" 页面
How to display a "not found" page with parameterised route in Durandal
我有一个 Durandal 应用程序,如果 URL 不对应于已知路线,我会使用 router.mapUnknownRoutes
显示用户友好的错误页面。这工作正常——如果我去,说 /foo
,并且它不匹配路由,那么由 mapUnknownRoutes
指定的模块被正确显示。
然而,当我有一个参数化路由时,我找不到任何方法来显示相同的错误页面,并且该参数与后端的任何内容都不匹配。
例如,假设我有一条类似 people/:slug
的路由,其中相应模块的 activate
方法如下所示:
this.activate = function (slug) {
dataService.load(slug).then(function () {
// ... set observables used by view ...
});
};
如果我去,说 /people/foo
,那么结果取决于 dataService.load('foo')
returns 数据或错误:
- 如果后端存在
foo
则没问题 - 已设置可观察对象并继续组合。
- 如果
foo
不存在,则抛出错误(因为不存在catch
)。这会导致未处理的错误,导致导航被取消并且路由器停止工作。
我知道我可以从 canActivate
return false
并且导航将以更简洁的方式取消,而不会阻塞路由器。然而,这不是我想要的;我想要一个无效的 URL 来告诉用户出了点问题。
我知道我可以 return { redirect: 'not-found' }
或类似 canActivate
的东西。然而,这很糟糕,因为它破坏了后退按钮——在重定向发生后,如果用户按下后退,他们会返回到 /people/foo
,这会导致另一个错误,因此另一个重定向回到 not-found
.
我尝试了几种不同的方法,主要涉及添加对承诺定义的 catch
调用:
this.activate = function (slug) {
dataService.load(slug).then(function () {
// ... set observables used by view ...
}).catch(function (err) {
// ... do something to indicate the error ...
});
};
activate
(或canActivate
)能否通知路由器该路由实际上是无效的,就好像它一开始就没有匹配一样?
activate
(或canActivate
)是否可以发布一个rewrite
(而不是redirect
)以便路由器显示不同的模块而不改变URL?
- 我可以直接
compose
其他模块代替当前模块(并取消当前模块的合成)吗?
我也试过一个空的 catch
块,它吞没了错误(我可以在这里添加一个 toast 来通知用户,这总比没有好)。但是,这会导致很多绑定错误,因为从未设置视图期望的可观察对象。可能我可以将整个视图包装在 if
绑定中以防止错误,但这会导致空白页面而不是错误消息;或者我必须将错误消息放入可能无法检索其数据的每个视图中。无论哪种方式,这都是视图污染而不是 DRY,因为我应该只写一次 "not found" 错误消息。
我只想要一个无效的 URL(特别是匹配路由但包含无效参数值的 URL)来显示显示 "page not found" 的页面。这肯定也是其他人想要的东西吧?有什么办法可以实现吗?
我认为您应该能够使用 activate
或 canActivate
方法中的以下内容。
router.navigate('not-found', {replace: true});
事实证明,内森的回答虽然不太正确,但让我走上了正轨。我所做的似乎有点老套,但确实有效。
有两个选项可以传递给 router.navigate()
- replace
和 trigger
。传递 replace
(默认为 false
)在使用 pushState
和 replaceState
(或使用哈希更改事件模拟相同)的历史插件之间切换。传递 trigger
(默认为 true
)会在实际加载视图(并更改 URL)与仅更改地址栏中的 URL 之间切换。这看起来像我想要的,只是错误的方式 - 我想在不更改 URL.
的情况下加载不同的视图
(文档中有一些相关信息,但不是很彻底:http://durandaljs.com/documentation/Using-The-Router.html)
我的解决方案是导航到 not-found
模块并激活它,然后在不触发激活的情况下导航回原来的 URL。
所以在我执行数据库查找的模块中,在它的 activate
中,如果找不到记录,我调用:
router.navigate('not-found?realUrl=' + document.location.pathname + document.location.hash, { replace: true, trigger: true });
(我意识到 trigger: true
是多余的,但它使它变得明确)。
然后在 not-found
模块中,它有一个 activate
看起来像:
if (params.realUrl) {
router.navigate(params.realUrl, { replace: true, trigger: false });
}
用户看到的是,它重定向到 not-found?realUrl=people/joe
,然后 URL 立即变回 people/joe
,而 not-found
模块仍然显示。因为这些都是 replace
风格的导航,如果用户向后导航,他们会转到上一个条目,这是他们在点击损坏的 link 之前来自的页面(即后退按钮应该做什么)做)。
就像我说的,这看起来很老套,我不喜欢 URL 闪烁,但这似乎是我能做的最好的,而且大多数人不会注意到地址栏。
我有一个 Durandal 应用程序,如果 URL 不对应于已知路线,我会使用 router.mapUnknownRoutes
显示用户友好的错误页面。这工作正常——如果我去,说 /foo
,并且它不匹配路由,那么由 mapUnknownRoutes
指定的模块被正确显示。
然而,当我有一个参数化路由时,我找不到任何方法来显示相同的错误页面,并且该参数与后端的任何内容都不匹配。
例如,假设我有一条类似 people/:slug
的路由,其中相应模块的 activate
方法如下所示:
this.activate = function (slug) {
dataService.load(slug).then(function () {
// ... set observables used by view ...
});
};
如果我去,说 /people/foo
,那么结果取决于 dataService.load('foo')
returns 数据或错误:
- 如果后端存在
foo
则没问题 - 已设置可观察对象并继续组合。 - 如果
foo
不存在,则抛出错误(因为不存在catch
)。这会导致未处理的错误,导致导航被取消并且路由器停止工作。
我知道我可以从 canActivate
return false
并且导航将以更简洁的方式取消,而不会阻塞路由器。然而,这不是我想要的;我想要一个无效的 URL 来告诉用户出了点问题。
我知道我可以 return { redirect: 'not-found' }
或类似 canActivate
的东西。然而,这很糟糕,因为它破坏了后退按钮——在重定向发生后,如果用户按下后退,他们会返回到 /people/foo
,这会导致另一个错误,因此另一个重定向回到 not-found
.
我尝试了几种不同的方法,主要涉及添加对承诺定义的 catch
调用:
this.activate = function (slug) {
dataService.load(slug).then(function () {
// ... set observables used by view ...
}).catch(function (err) {
// ... do something to indicate the error ...
});
};
activate
(或canActivate
)能否通知路由器该路由实际上是无效的,就好像它一开始就没有匹配一样?activate
(或canActivate
)是否可以发布一个rewrite
(而不是redirect
)以便路由器显示不同的模块而不改变URL?- 我可以直接
compose
其他模块代替当前模块(并取消当前模块的合成)吗?
我也试过一个空的 catch
块,它吞没了错误(我可以在这里添加一个 toast 来通知用户,这总比没有好)。但是,这会导致很多绑定错误,因为从未设置视图期望的可观察对象。可能我可以将整个视图包装在 if
绑定中以防止错误,但这会导致空白页面而不是错误消息;或者我必须将错误消息放入可能无法检索其数据的每个视图中。无论哪种方式,这都是视图污染而不是 DRY,因为我应该只写一次 "not found" 错误消息。
我只想要一个无效的 URL(特别是匹配路由但包含无效参数值的 URL)来显示显示 "page not found" 的页面。这肯定也是其他人想要的东西吧?有什么办法可以实现吗?
我认为您应该能够使用 activate
或 canActivate
方法中的以下内容。
router.navigate('not-found', {replace: true});
事实证明,内森的回答虽然不太正确,但让我走上了正轨。我所做的似乎有点老套,但确实有效。
有两个选项可以传递给 router.navigate()
- replace
和 trigger
。传递 replace
(默认为 false
)在使用 pushState
和 replaceState
(或使用哈希更改事件模拟相同)的历史插件之间切换。传递 trigger
(默认为 true
)会在实际加载视图(并更改 URL)与仅更改地址栏中的 URL 之间切换。这看起来像我想要的,只是错误的方式 - 我想在不更改 URL.
(文档中有一些相关信息,但不是很彻底:http://durandaljs.com/documentation/Using-The-Router.html)
我的解决方案是导航到 not-found
模块并激活它,然后在不触发激活的情况下导航回原来的 URL。
所以在我执行数据库查找的模块中,在它的 activate
中,如果找不到记录,我调用:
router.navigate('not-found?realUrl=' + document.location.pathname + document.location.hash, { replace: true, trigger: true });
(我意识到 trigger: true
是多余的,但它使它变得明确)。
然后在 not-found
模块中,它有一个 activate
看起来像:
if (params.realUrl) {
router.navigate(params.realUrl, { replace: true, trigger: false });
}
用户看到的是,它重定向到 not-found?realUrl=people/joe
,然后 URL 立即变回 people/joe
,而 not-found
模块仍然显示。因为这些都是 replace
风格的导航,如果用户向后导航,他们会转到上一个条目,这是他们在点击损坏的 link 之前来自的页面(即后退按钮应该做什么)做)。
就像我说的,这看起来很老套,我不喜欢 URL 闪烁,但这似乎是我能做的最好的,而且大多数人不会注意到地址栏。