Made typos in routes? Redirect routes with functions

Connie Leung - Jun 23 - - Dev Community

Introduction

In this blog post, I want to describe a new feature called redirect functions with routes. When defining routes in Angular, it is possible to catch and redirect a route to a different path using redirectTo. One example is to catch all unknown routes and redirect them to a 404 page. Another example is redirecting a default route to a home page. In Angular 18, redirectTo is enhanced to accept a function. Then, a route can perform logic in the function and route to different paths according to some criteria.

I will demonstrate how I made typos in the routes and used the redirect routes to functions technique to redirect them to the routes with the correct spelling.

Update Application Config to add routing

// app.config.ts 

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    provideRouter(routes, withComponentInputBinding()),
    provideExperimentalZonelessChangeDetection()
  ]
};
Enter fullscreen mode Exit fullscreen mode

provideRouter(routes, withComponentInputBinding()) add routes to the demo to navigate to Pokemon List and Pokemon Details respectively.

// main.ts

import { appConfig } from './app.config';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  template: `
    <h2>redirectTo Demo</h2>
    <router-outlet />
  `,
})
export class App {}

bootstrapApplication(App, appConfig);
Enter fullscreen mode Exit fullscreen mode

Bootstrap the component and the application configuration to start the Angular application.

Create routes for navigation to the Pokemon list and Pokemon Details

I wanted to add two new routes to load the components but made typos in the configuration. I typed /pokermon-list instead of /pokemon-list. Similarly, I typed /pokermon-list/:name instead of the correct spelling /pokemon-list/:name

// app.route.ts

// app.route.ts

export const routes: Routes = [
    {
        path: 'pokermon-list',
        loadComponent: () => import('./pokemons/pokemon-list/pokemon-list.component'),
        title: 'Pokermon List',
    }, 
   {
        path: 'pokemon-list/:name',
        loadComponent: () => import('./pokemons/pokemon/pokemon.component'),
        title: 'Pokermon',
    },
   {
        path: '',
        pathMatch: 'full',
        redirectTo: 'pokermon-list'
    },
    {
        path: '**',
        redirectTo: 'pokermon-list'
    }
];
Enter fullscreen mode Exit fullscreen mode

The careless mistakes went unnoticed, and I copied and pasted the erroneous routes to the routeLink in the inline templates.

Add routing in the template

// pokemon-list.component.ts

<div class="container">
    @for(p of pokemons(); track p.name) {
       <div class="card">
          <p>Name: <a [routerLink]="['/pokermon-list', p.name]">{{ p.name }}</a></p>
       </div>
    }
</div>
Enter fullscreen mode Exit fullscreen mode

In the Pokemon List component, the first element of the routerLink is /pokermon-list.

// pokemon.component.ts

<div>
   <a [routerLink]="'/pokermon-list'">Back</a>
</div>
Enter fullscreen mode Exit fullscreen mode

In the Pokemon component, the value of the routerLink is /pokermon-list to go back to the previous page.

Someone spotted the typos in the URL and informed me. I easily fixed the typos in the routerLink and the routes array.

Fix the errors in routes

// app.routes.ts

import { Routes } from '@angular/router';

export const routes: Routes = [
    {
        path: 'pokemon-list',
        loadComponent: () => import('./pokemons/pokemon-list/pokemon-list.component'),
        title: 'Pokermon List',
    }, 
    {
        path: 'pokemon-list/:name',
        loadComponent: () => import('./pokemons/pokemon/pokemon.component'),
        title: 'Pokermon',
    }, 
    {
        path: '',
        pathMatch: 'full',
        redirectTo: 'pokemon-list'
    },
    {
        path: '**',
        redirectTo: 'pokemon-list'
    }
];
Enter fullscreen mode Exit fullscreen mode

All occurrences of pokermon-list were replaced with pokemon-list in the routes array.

// pokemon-list.component.ts

div class="container">
   @for(p of pokemons(); track p.name) {
      <div class="card">
          <p>Name: <a [routerLink]="['/pokemon-list', p.name]">{{ p.name }}</a></p>
     </div>
   }
</div>
Enter fullscreen mode Exit fullscreen mode
// pokemon.component.ts
<div>
      <a [routerLink]="'/pokemon-list'">Back</a>
</div>
Enter fullscreen mode Exit fullscreen mode

Similarly, the templates replaced /pokermon-list with /pokemon-list in the routerLink inputs.

This should work, but I wanted to improve my new solution. If someone bookmarked /pokermon-list or /pokermon-list/pikachu before, these URLs do not work now. Instead, the URLs must redirect to /pokemon-list and /pokemon-list/pikachu.

I will show you how it can be done by using the new redirectTo function in Angular 18.

Redirect routes with function

// app.routes.ts

export const routes: Routes = [
    {
        path: 'pokemon-list',
        loadComponent: () => import('./pokemons/pokemon-list/pokemon-list.component'),
        title: 'Pokermon List',
    }, 
    {
        path: 'pokemon-list/:name',
        loadComponent: () => import('./pokemons/pokemon/pokemon.component'),
        title: 'Pokermon',
    }, 
    {
        path: 'pokermon-list',
        redirectTo: 'pokemon-list',
    },
    {
        path: 'pokermon-list/:name',
        redirectTo: ({ params }) => {
            const name = params?.['name'] || '';
            return name ? `/pokemon-list/${name}` : 'pokemon-list';
        }
    },
    {
        path: '',
        pathMatch: 'full',
        redirectTo: 'pokemon-list'
    },
    {
        path: '**',
        redirectTo: 'pokemon-list'
    }
];
Enter fullscreen mode Exit fullscreen mode

redirectTo accepts a string or a function; therefore, I redirected /pokermon-list to /pokemon-list. This is the same behavior before Angular 18.

The /pokermon-list/:name route was tricky because I wanted to redirect to /pokemon-list/:name when the name route parameter was present and to /pokemon-list when the name route parameter was missing. I satisfied the requirements by redirecting the route with a function.

{
        path: 'pokermon-list/:name',
        redirectTo: ({ params }) => {
            const name = params?.['name'] || '';
            return name ? `/pokemon-list/${name}` : 'pokemon-list';
        }
 },
Enter fullscreen mode Exit fullscreen mode

I de-structured params from ActivatedRouteSnapshot and found the value of the name property in the record. When the string was not empty, users were directed to see the Pokemon details, and when it was empty, they were redirected to the Pokemon list.

The full listing of the routes array

// app.routes.ts

import { Routes } from '@angular/router';

export const routes: Routes = [
    {
        path: 'pokemon-list',
        loadComponent: () => import('./pokemons/pokemon-list/pokemon-list.component'),
        title: 'Pokermon List',
    }, 
    {
        path: 'pokemon-list/:name',
        loadComponent: () => import('./pokemons/pokemon/pokemon.component'),
        title: 'Pokermon',
    }, 
    {
        path: 'pokermon-list',
        redirectTo: 'pokemon-list',
    },
    {
        path: 'pokermon-list/:name',
        redirectTo: ({ params }) => {
            const name = params?.['name'] || '';
            return name ? `/pokemon-list/${name}` : 'pokemon-list';
        }
    },
    {
        path: '',
        pathMatch: 'full',
        redirectTo: 'pokemon-list'
    },
    {
        path: '**',
        redirectTo: 'pokemon-list'
    }
];
Enter fullscreen mode Exit fullscreen mode

This is the full listing of the routes array where navigation and redirection work as expected.

The following Stackblitz repo displays the

This is the end of the blog post that describes redirecting routes with redirect functions in Angular. I hope you like the content and continue to follow my learning experience in Angular, NestJS, GenerativeAI, and other technologies.

Resources:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .