Angular Universal 需要 initialNavigation=enabled,这会破坏 APP_INITIALIZER 和 Guards
Angular Universal requires initialNavigation=enabled which breaks APP_INITIALIZER and Guards
在我们的应用程序中添加 Angular 通用时,我们必须在路由器上设置 initialNavigation="enabled"
标志,以避免闪烁。
现在这给我们带来了 2 个问题:
- 我们动态创建路由,现在打开其中一个路由失败
- 因为应用程序不等待
APP_INITIALIZER
加载,所以无法加载受保护的路由,因为保护总是假定用户未经授权,因为检查发生在 APP_INITIALIZER
我发现了几个 Github 问题(即 https://github.com/angular/universal/issues/1623),但其中 none 确实提供了解决方案。
如何在等待 APP_INITIALIZER
执行的同时使用 initialNavigation="enabled"
?
编辑 (01/02/2021):在 Angular 11 中,措辞已更改,该选项现在称为 enabledBlocking
。不过此处提到的问题与此无关。
我也会提供我在这里找到的解决方案。我还将其作为 issue 发布在 Angular Universal 的 Github 存储库中。如果对 Universal 进行了更改,这将使这更容易,我将更新此答案。
解决方法:
基本上我现在所做的是在 Angular 应用程序完全启动之前,在服务器和应用程序中获取有关页面的数据。在 app-routing.modules
-constructor 中改变 routes
-array 显然足以在路由器进行初始导航之前获取动态路由。
它看起来或多或少像这样(正如 Nicolae 所说,这可以重构以避免重复代码):
server.ts
:
server.get('*', (req, res) => {
// fetch dynamic routes
// /!\ duplicate code to src/main.ts
fetch('http://static.content/')
.then(response => response.json())
.then(resp => {
const routes = resp.entries.map(route => ({
path: route.path,
component: StaticContentComponent,
data: {
id: route._id,
name: route.name
}
}));
res.render(indexHtml, {
req,
providers: [
{ provide: APP_BASE_HREF, useValue: req.baseUrl },
{ provide: DYNAMIC_ROUTES, useValue: routes }
]
});
});
});
return server;
}
与main.ts
基本相同:
document.addEventListener('DOMContentLoaded', () => {
// fetch dynamic routes
// /!\ duplicate code to server.ts
fetch('http://static.content/')
.then(response => response.json())
.then(resp => {
const routes = resp.entries.map(route => ({
path: route.path,
component: StaticContentComponent,
data: {
id: route._id,
name: route.name
}
}));
platformBrowserDynamic([
{ provide: DYNAMIC_ROUTES, useValue: routes }
])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
});
});
然后在我的 app-routing.module.ts
中,我将 DYNAMIC_ROUTES
中提供的数据添加到路线中:
const DYNAMIC_ROUTES = new InjectionToken<IEnvironment>('dynamicRoutes');
@NgModule({
imports: [
RouterModule.forRoot(routes, {
initialNavigation: 'enabled'
})
],
exports: [RouterModule]
})
export class AppRoutingModule {
constructor(@Inject(DYNAMIC_ROUTES) private dynamicRoutes, private router: Router) {
const config = router.config;
config.unshift(...this.dynamicRoutes);
this.router.resetConfig(config);
}
}
- 'enabledBlocking' - 初始导航在根之前开始
组件被创建。 bootstrap 被阻塞直到初始
导航完成。服务器端需要此值
渲染工作。
- 'enabledNonBlocking' -(默认)初始导航在之后开始
根组件已创建。 bootstrap 未被阻止
在完成初始
希望对您有所帮助
RouterModule.forRoot(routes, {
relativeLinkResolution: 'corrected',
initialNavigation: 'enabledBlocking',
useHash: true
}),
在我们的应用程序中添加 Angular 通用时,我们必须在路由器上设置 initialNavigation="enabled"
标志,以避免闪烁。
现在这给我们带来了 2 个问题:
- 我们动态创建路由,现在打开其中一个路由失败
- 因为应用程序不等待
APP_INITIALIZER
加载,所以无法加载受保护的路由,因为保护总是假定用户未经授权,因为检查发生在APP_INITIALIZER
我发现了几个 Github 问题(即 https://github.com/angular/universal/issues/1623),但其中 none 确实提供了解决方案。
如何在等待 APP_INITIALIZER
执行的同时使用 initialNavigation="enabled"
?
编辑 (01/02/2021):在 Angular 11 中,措辞已更改,该选项现在称为 enabledBlocking
。不过此处提到的问题与此无关。
我也会提供我在这里找到的解决方案。我还将其作为 issue 发布在 Angular Universal 的 Github 存储库中。如果对 Universal 进行了更改,这将使这更容易,我将更新此答案。
解决方法:
基本上我现在所做的是在 Angular 应用程序完全启动之前,在服务器和应用程序中获取有关页面的数据。在 app-routing.modules
-constructor 中改变 routes
-array 显然足以在路由器进行初始导航之前获取动态路由。
它看起来或多或少像这样(正如 Nicolae 所说,这可以重构以避免重复代码):
server.ts
:
server.get('*', (req, res) => {
// fetch dynamic routes
// /!\ duplicate code to src/main.ts
fetch('http://static.content/')
.then(response => response.json())
.then(resp => {
const routes = resp.entries.map(route => ({
path: route.path,
component: StaticContentComponent,
data: {
id: route._id,
name: route.name
}
}));
res.render(indexHtml, {
req,
providers: [
{ provide: APP_BASE_HREF, useValue: req.baseUrl },
{ provide: DYNAMIC_ROUTES, useValue: routes }
]
});
});
});
return server;
}
与main.ts
基本相同:
document.addEventListener('DOMContentLoaded', () => {
// fetch dynamic routes
// /!\ duplicate code to server.ts
fetch('http://static.content/')
.then(response => response.json())
.then(resp => {
const routes = resp.entries.map(route => ({
path: route.path,
component: StaticContentComponent,
data: {
id: route._id,
name: route.name
}
}));
platformBrowserDynamic([
{ provide: DYNAMIC_ROUTES, useValue: routes }
])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
});
});
然后在我的 app-routing.module.ts
中,我将 DYNAMIC_ROUTES
中提供的数据添加到路线中:
const DYNAMIC_ROUTES = new InjectionToken<IEnvironment>('dynamicRoutes');
@NgModule({
imports: [
RouterModule.forRoot(routes, {
initialNavigation: 'enabled'
})
],
exports: [RouterModule]
})
export class AppRoutingModule {
constructor(@Inject(DYNAMIC_ROUTES) private dynamicRoutes, private router: Router) {
const config = router.config;
config.unshift(...this.dynamicRoutes);
this.router.resetConfig(config);
}
}
- 'enabledBlocking' - 初始导航在根之前开始 组件被创建。 bootstrap 被阻塞直到初始 导航完成。服务器端需要此值 渲染工作。
- 'enabledNonBlocking' -(默认)初始导航在之后开始 根组件已创建。 bootstrap 未被阻止 在完成初始
希望对您有所帮助
RouterModule.forRoot(routes, { relativeLinkResolution: 'corrected', initialNavigation: 'enabledBlocking', useHash: true }),