Angular routing - route to success for beginners

tomwebwalker

Tomasz Flis

Posted on May 24, 2023

Angular routing - route to success for beginners

Introduction

In the current frontend development approach, most web applications are SPA which stands for Single Page applications. It means the browser won't refresh the page whenever we redirect from one page to another. Instead, the proper content of the page is replaced. In this article, I will explain features placed in Angular and related to the routing mechanism.

Project creation

To create a new project in the command line, I type:

ng new books-app --standalone
Enter fullscreen mode Exit fullscreen mode

And I answer y to create Angular routing.
standalone is a new flag added with version 14. It creates an application in a standalone mode, so for example, AppModule is not created, and AppComponent is used as an initial class.

Since we do not have AppModule, We need a provider for routes, which are provided by using provideRouter function inside the app.config.ts file. Thankfully, that is auto-generated during the project initialization.

Route properties

Inside the app.routes.ts file, we will find routes constants with an empty array. For demo purposes, let's generate a component that will be routed to. We can do that by command:

ng g c library
Enter fullscreen mode Exit fullscreen mode
  • g is a shortcut for generate
  • c is a shortcut for component
  • library is the name of the component

Since we defined our project as a standalone, that component is also generated as a standalone.

Path

Properties related to the path are:

  • path - string represents a link to our component, so setting it to library allows us to visit that component under the http//:example.com/library address.

  • redirectTo - string which will redirect the current path to the given route. For example, we can set the path to the wildcard ** to redirect to the given path in redirectTo

  • pathMatch - can be set to one of the:

    • full - means that the whole URL path needs to match and is consumed by the route matching algorithm.
    • prefix means the first route where the path matches the start of the URL chosen. Still, the route matching algorithm continues searching for matching child routes where the rest of the URL matches.
  • matcher - if we cannot define a route by a simple string, we can define a custom matcher that, for example, can check a given URL with a regex (we cannot use it with a path property). Sample from the documentation:

{
  matcher: (url) => {
    if (url.length === 1 && url[0].path.match(/^@[\w]+$/gm)) {
      return {
        consumed: url,
        posParams: {
          username: new UrlSegment(url[0].path.slice(1), {})
        }
      };
    }

    return null;
  },
  component: ProfileComponent
}
Enter fullscreen mode Exit fullscreen mode

Component

Properties related to the component are:

  • loadComponent - is used to lazy load selected components whenever the given route is visited. Great for optimization because it creates a dedicated bundle file for a specific component, so the initial bundle file is smaller; example value can be: () => import('./library.component').then(m => m.LibraryComponent),
  • component - like above, but without lazy loading example value: LibraryComponent,
  • children - array with nested routes under a given route,
  • loadChildren - like above but for modules.

Guards and resolvers

In Angular, Guards are used to protect routes, for example, if a user is not allowed to visit some of them. To create a guard, we can use CLI by typing:

ng g g admin-only
Enter fullscreen mode Exit fullscreen mode

It can be set to one (or more) of possible types:

  • CanActivate - which loads component only when the given guards return true
  • CanActivateChild - which loads children of the given routes only whenever the provided guard returns true, but it won't check the component set to the given route
  • CanDeactivate - which checks if we are allowed to leave the specific route
  • CanMatch - introduced with version 14 of the angular, checks if we can enter a given route. If not, then proceeds to the next one that matches the current path (in CanActive, it would redirect back). Example content of the guard can be:
export const adminOnlyGuard: CanMatchFn = (route, segments) => {
  const user = localStorage.getItem('user');
  if (!user) {
    localStorage.setItem('user', 'John Doe');
    return false;
  }
  return true;
};
Enter fullscreen mode Exit fullscreen mode

which, for the first time, won't let the user in because there is no user data yet, but after a second attempt, it will allow them to enter.
To use the generated guard, we have to apply it in appropriate property or more than one if needed, for example:

canActivate: [adminOnlyGuard],
canMatch: [adminOnlyGuard],
Enter fullscreen mode Exit fullscreen mode

Resolvers are used to provide data to the component that is set in the current route. It will wait for the result from that resolver and then loads the component, so thanks to that, we are sure that the required values will be there. To generate a resolver, we can use CLI once again:

ng g r user-data
Enter fullscreen mode Exit fullscreen mode

This will create a method that is used to retrieve data. Example content could be:

export const userDataResolver: ResolveFn<string> = (route, state) => {
  return of('mail@example.com').pipe(delay(3000))
};

Enter fullscreen mode Exit fullscreen mode

of is an RxJs function to create an observable, and a delay is a function to delay the emission from the observable by a given time.
To use the generated resolver, we have to add it to the resolver property:

resolve: [userDataResolver]
Enter fullscreen mode Exit fullscreen mode

To reach that data from the component, we can use the ActivatedRoute snapshot instance, for example:

  private readonly route = inject(ActivatedRoute);

  constructor() {
    console.log(this.route.snapshot.data);
  }
Enter fullscreen mode Exit fullscreen mode

To distinguish whether to run resolvers or guards, we can use the runGuardsAndResolvers property, which can be set to one of the:

  • always - which will always run
  • pathParamsChange - whenever path params are changed
  • paramsOrQueryParamsChange - whenever path, matrix, or query parameters change
  • pathParamsOrQueryParamsChange - Rerun guards and resolvers when the path params change, or query params have changed.

Data

To pass static data to the component, we can use the data property and pass any data as a value, for example:

data: {
  prop: 'value'
}
Enter fullscreen mode Exit fullscreen mode

To reach that data from the component, we can use ActivatedRoute like above.
The title property sets the page title of a given route.
The providers property defines instances of providers, for example, services.
We can also pass data as route segments. To do that, in the path property, we can use a colon : NAME, for example, library/:id, which then in route /library/1 sets key id and a value 1. That value can be found in the ActivatedRoute but in the snapshot.params property.

Outlet

To display the component assigned to the route in the given parent component, we are placing the following:

<router-outlet></router-outlet>
Enter fullscreen mode Exit fullscreen mode

That will render the contents of the component in the selected place.
We can have more than one router-outlet, and then we can select where to render the view by adding a name attribute to the html part:

<router-outlet name="secondary"></router-outlet>
Enter fullscreen mode Exit fullscreen mode

and by setting route property:

outlet: 'secondary'
Enter fullscreen mode Exit fullscreen mode

Route navigations

We can change the current route from HTML by using the routerLink attribute and setting it to the required path, for example:

<a href="" routerLink="/other">Go to other</a>
Enter fullscreen mode Exit fullscreen mode

and from the typescript code part by using Router and calling:

  private readonly router = inject(Router);

  gotToOther() {
    this.router.navigate(['other']);
  }
Enter fullscreen mode Exit fullscreen mode

Summary

The angular framework has a powerful routing mechanism and can be configured in many ways. I introduced some basics in this article and encourage You to dive deep into the official documentation.

💖 💪 🙅 🚩
tomwebwalker
Tomasz Flis

Posted on May 24, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related