Improve your angular routing toolbox – URL ROUTE Matcher

I

Why should you learn about the Route Matcher?

To obtain a greater degree of flexibility and control over Angular routing. You’ll have the tricks to build complex nested routes, to validate route params (e.g.: match a ZipCodeComponent only if the route param is a valid zip code), to differentiate routing scenarios (e.g.: if a route parameter is a valid UUID match the UserProfileComponent , if it’s a string starting with @, render UserFeedComponent), and to prevent a same-route component from being destroyed and recreated on route changes.

I also find the URL matcher to the perfect tool for implementing complex microfrontend architectures, regardless of framework, but we’ll cover that in a later piece.

How to use it?

Want something interactive? Jump to the bottom

The matcher function can be added on a Route, and cannot be used together with the path property of the route. The typings won’t catch it and you’ll get a runtime error.

export const DemoRoutes: Routes = [
  {
    path: 'my-path',
    component: MyComponent,
  },
  {
    matcher: (segments: UrlSegment[]) => {
      if (segments.length !== 1 || segments[0].path !== 'my-path') {
        return null;
      }

      return { consumed: segments };
    },
    component: MyComponent,
  },
];

The function receives an array of URL segments (in essence, the pieces composing a path split by '/'`), a group of URL segments, and the route itself. It must return null if no match is found, and an object with the consumed segments if we do find a match. Additionally, it can include posParams which are the route params.

When using the URL matcher on a parent route, consume only the parent segments so the child routes can receive the others.

Example scenarios

Let’s say we’re building a web app involving a file browser UI with entities for disk, folder, and the file itself. The new feature should allow viewing and navigating between this structure, as well as sharing the link of a file. We’ll display every listing using the same component.

How would we define the routing for this new location feature?

export const LocationDemoRoutes: Routes = [
  {
    path: 'location',
    component: LocationComponent,
  },
  {
    path: 'location/:disk',
    component: LocationComponent,
  },
  {
    path: 'location/:disk/:folder',
    component: LocationComponent,
  },
  {
    path: 'location/:disk/:folder/:file',
    component: LocationComponent,
  },
];

This would be the first idea. However, with this kind of routing, navigating up or down a level with destroy and recreate the component.

Using the matcher function, we can define something like this:

export const LocationMatcherRoutes: Routes = [
  {
    matcher: locationMatcher,
    component: LocationComponent,
  },
];

function locationMatcher(segments: UrlSegment[]) {
  if (!matchLocationRoute(segments)) {
    return null;
  }

  return {
    consumed: segments,
    posParams: extractParams(...segments.slice(1, 4)),
  };
}

Practical example

Make sure to check out this repository and this small app showcasing some of the things covered in this article.

By ro.an.m