Angular Universal 需要initialNavigation=enabled,这会破坏 APP_INITIALIZER 和 Guards

tom*_*ler 10 angular-universal angular

在将 Angular Universal 添加到我们的应用程序时,我们必须initialNavigation="enabled"在路由器上设置标志,以避免闪烁。

现在这给我们带来了两个问题:

  1. 我们动态创建路线,打开其中一条路线现在失败
  2. 因为应用程序不会等待APP_INITIALIZERs 加载,所以无法加载受保护的路由,因为守卫总是假设用户未经授权,因为检查发生在APP_INITIALIZER

我发现了几个与此相关的 Github 问题(即https://github.com/angular/universal/issues/1623),但没有一个真正提供了解决方案。

如何使用initialNavigation="enabled"while 同时等待APP_INITIALIZERs 执行?

编辑(01/02/2021):在 Angular 11 中,措辞已更改,该选项现在称为enabledBlocking。然而这里提到的问题并没有涉及到这一点。

tom*_*ler 2

我也会提供我在这里找到的解决方案。我还将其作为问题发布在 Angular Universal 的 Github 存储库中。如果对通用进行了更改,这将使这变得更容易,我将更新此答案。

解决方案:基本上我现在所做的就是在 Angular 应用程序启动之前获取服务器和应用程序中有关页面的数据。在路由器进行初始导航之前,更改-constructorroutes内的 -arrayapp-routing.modules显然足以尽早获取动态路由。

它看起来或多或少像这样(正如 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;
}
Run Code Online (Sandbox Code Playgroud)

和基本相同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));
    });
});
Run Code Online (Sandbox Code Playgroud)

然后在我的路径中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);
  }
}
Run Code Online (Sandbox Code Playgroud)