Skip to content

Commit

Permalink
feat(@angular/ssr): Add support for route matchers with fine-grained …
Browse files Browse the repository at this point in the history
…render mode control

This commit adds support for custom route matchers in Angular SSR, allowing fine-grained control over the `renderMode` (Server, Client) for individual routes, including those defined with matchers.

Routes with custom matchers are **not** supported during prerendering and must explicitly define a `renderMode` of either server or client.

The following configuration demonstrates how to use glob patterns (including recursive `**`) to define server-side rendering (SSR) or client-side rendering (CSR) for specific parts of the 'product' route and its child routes.

```typescript
// app.routes.ts
import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    component: DummyComponent,
  },
  {
    path: 'product',
    component: DummyComponent,
    children: [
      {
        path: '',
        component: DummyComponent,
      },
      {
        path: 'list',
        component: DummyComponent,
      },
      {
        matcher: () => null, // Example custom matcher (always returns null)
        component: DummyComponent,
      },
    ],
  },
];
```

```typescript
// app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  { path: '**', renderMode: RenderMode.Client },
  { path: 'product', renderMode: RenderMode.Prerender },
  { path: 'product/list', renderMode: RenderMode.Prerender },
  { path: 'product/**/overview/details', renderMode: RenderMode.Server },
];
```

Closes angular#29284
  • Loading branch information
alan-agius4 committed Jan 29, 2025
1 parent 832bfff commit 43fce31
Showing 1 changed file with 66 additions and 0 deletions.
66 changes: 66 additions & 0 deletions packages/angular/ssr/test/routes/ng-routes_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ describe('extractRoutesAndCreateRouteTree', () => {
`The 'invalid' route does not match any route defined in the server routing configuration`,
);
});

it('should error when a route with a matcher when render mode is Prerender.', async () => {
setAngularAppTestingManifest(
[{ matcher: () => null, component: DummyComponent }],
[
{
path: '**',
renderMode: RenderMode.Prerender,
},
],
);

const { errors } = await extractRoutesAndCreateRouteTree({ url });
expect(errors[0]).toContain(
`The route '**' is set for prerendering but has a defined matcher. ` +
`Routes with matchers cannot use prerendering. Please specify a different 'renderMode'.`,
);
});
});

describe('when `invokeGetPrerenderParams` is true', () => {
Expand Down Expand Up @@ -330,6 +348,54 @@ describe('extractRoutesAndCreateRouteTree', () => {
});
});

it('should extract routes with a route level matcher', async () => {
setAngularAppTestingManifest(
[
{
path: '',
component: DummyComponent,
},
{
path: 'product',
component: DummyComponent,
children: [
{
path: '',
component: DummyComponent,
},
{
matcher: () => null,
component: DummyComponent,
},
{
path: 'list',
component: DummyComponent,
},
],
},
],
[
{ path: '**', renderMode: RenderMode.Client },
{ path: 'product', renderMode: RenderMode.Client },
{ path: 'product/*', renderMode: RenderMode.Client },
{ path: 'product/**/overview/details', renderMode: RenderMode.Server },
{ path: 'product/**/overview', renderMode: RenderMode.Server },
{ path: 'product/**/overview/about', renderMode: RenderMode.Server },
],
);

const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url });
expect(errors).toHaveSize(0);
expect(routeTree.toObject()).toEqual([
{ route: '/', renderMode: RenderMode.Client },
{ route: '/product', renderMode: RenderMode.Client },
{ route: '/product/**/overview', renderMode: RenderMode.Server },
{ route: '/product/**/overview/details', renderMode: RenderMode.Server },
{ route: '/product/**/overview/about', renderMode: RenderMode.Server },
{ route: '/product/list', renderMode: RenderMode.Client },
]);
});

it('should extract nested redirects that are not explicitly defined.', async () => {
setAngularAppTestingManifest(
[
Expand Down

0 comments on commit 43fce31

Please sign in to comment.