Angular routing - route to success for beginners
Tags: webdev, javascript, beginners, angular
Published: 2023-05-24 16:28:36

### 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
```
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
```
* `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:
```typescript
{
  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
}
```

#### 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
```
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:
```typescript
export const adminOnlyGuard: CanMatchFn = (route, segments) => {
  const user = localStorage.getItem('user');
  if (!user) {
    localStorage.setItem('user', 'John Doe');
    return false;
  }
  return true;
};
```
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:
```typescript
canActivate: [adminOnlyGuard],
canMatch: [adminOnlyGuard],
```


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
```
This will create a method that is used to retrieve data. Example content could be:
```typescript
export const userDataResolver: ResolveFn<string> = (route, state) => {
  return of('mail@example.com').pipe(delay(3000))
};

```
`of` is an RxJs [function](https://rxjs.dev/api/index/function/of) to create an observable, and a `delay` is a [function](https://rxjs.dev/api/index/function/delay) 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:
```typescript
resolve: [userDataResolver]
```
To reach that data from the component, we can use the `ActivatedRoute` snapshot instance, for example:
```typescript
  private readonly route = inject(ActivatedRoute);

  constructor() {
    console.log(this.route.snapshot.data);
  }
```
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:
```typescript
data: {
  prop: 'value'
}
```
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:
```html
<router-outlet></router-outlet>
```
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:
```html
<router-outlet name="secondary"></router-outlet>
```
and by setting route property:
```typescript
outlet: 'secondary'
```

### Route navigations
We can change the current route from HTML by using the `routerLink` attribute and setting it to the required path, for example:
```html
<a href="" routerLink="/other">Go to other</a>
```
and from the typescript code part by using `Router` and calling:
```typescript
  private readonly router = inject(Router);
  
  gotToOther() {
    this.router.navigate(['other']);
  }
```

### 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](https://angular.io/guide/router).