question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Impossible to use dynamic routes with Angular Universal?!

See original GitHub issue

Proposal

What is the summary of the proposal?

It should be possible to use dynamically generated routes with Angular Universal.

What is the proposal?

In the docs it clearly states that it is necessary for Angular Universal to work to use initialNavigation=enabled on the router. This, as the name already states, leads to (if I understand correctly) Angular performing the first navigation before i.e. the APP_INITIALIZERs are loaded (which seems strange to me in the first place).

Now I assume that a common use case for using Angular Universal is to add server side rendering to pages powered by some sort of headless CMS. That is also what I am trying to achieve. Without initialNavigation=enabled I had no problem to create the routes dynamically, after fetching page information from our backend. I basically execute this code within an APP_INITIALIZER in my app.modules.ts:

this.backendService.getStaticLinks().pipe(
  tap(routes => {
    const config = this.router.config;
    config.unshift(
      ...routes.map(route => ({
        path: route.path,
        component: StaticContentComponent,
        data: {
          id: route._id
        }
      }))
    );
    this.router.resetConfig(config);
  })
)

Now with initialNavigation=enabled I always receive a 404 because I have no way of adding the routes, before the app tries to navigate there. The app still works with Universal support if I do not use initialNavigation=enabled, but I get bad flickering, which is not acceptable to deliver it like this to the user.

Is there any way this problem can be solved?

edit: I am not entirely sure if this belongs to the Universal or the Angular Core team, so please feel free to move the ticket accordingly or let me know if I should open it somewhere else.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:12

github_iconTop GitHub Comments

2reactions
tommuellercommented, Sep 28, 2020

After a lot of reading and trial-and-error I now found a solution to this issue. I feel that it is a little bit hacky, so I would really love some feedback on it. Also I still believe that this should be possible with Angular Universal, without hacking into server.ts and main.ts.

Basically what I do now, is to fetch the data about the pages, both in the server and the app, before the Angular app gets bootstrapped at all. It look more or less like this:

server.ts:

// All regular routes use the Universal engine
  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;
}

and basically the same in 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));
    });
});

And then in my app-routing.module.ts I add the data provided in DYNAMIC_ROUTES to the routes:

@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);
  }
}

So this does actually work. I am a bit unhappy about having to make the call twice (but couldn’t get it working otherwise). Also I would have preferred to avoid hacking into server.ts and main.ts.

Any ideas on how to improve this? Or do you see any issues to take this into production?

0reactions
angular-automatic-lock-bot[bot]commented, Jun 23, 2022

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Angular universal : Unable to prerendering dynamic routes
There are many categories under my primary route, so I need this dynamic prerendering. How to set this type of data under prerender...
Read more >
Angular Dynamic Routing - Level Up Coding
The main trick is to use the ROUTES InjectionToken which is a low-level API for the router configuration. Instead of passing routes directly...
Read more >
Angular v9 & Universal: SSR and prerendering out of the box!
Prerendering your static routes is easy, except if you are using lazy loaded routes, these are not easy to guess by guess-parser ....
Read more >
Server-side rendering (SSR) with Angular Universal
Easily prepare an application for server-side rendering using the Angular CLI. The CLI schematic @nguniversal/express-engine performs the required steps, ...
Read more >
Dynamic Routing in Angular 12 | HTMLGoodies.com
As the name suggests, the primary purpose of Angular Route Guards is to guard access to a certain route, such as an authenticated...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found