# Router

## About

{% embed url="<https://angular.dev/guide/routing/common-router-tasks>" %}
routing/common-router-tasks
{% endembed %}

{% embed url="<https://angular.dev/guide/routing/router-reference#activated-route>" %}
routing/activated-route
{% endembed %}

{% hint style="warning" %}
*Servers which will host the deploy will need to, on 404 error, redirect to* `index.php` *to garantee that Angular SPA will take over the routing.*
{% endhint %}

Tipically the routes will be inside a file `app.routes.ts`.

{% hint style="info" %}
Routes order is important since `Router` uses a first-match wins strategy.

* More specific routes should be placed above less specific (more generic) routes.
* Place `wildcard` `**` routes last.
  {% endhint %}

{% hint style="warning" %}
**Url changes on a Component will re-use the Component instance.**&#x20;

This means that Signals or Observables are needed to catch changes on the URL like `/user/1 -> /user/2`.
{% endhint %}

{% code title="app.routes.ts" %}

```typescript
const appRoutes: Routes = [
    { path: '', component: HomeComponent },
    { path: 'employees', component: EmployeesComponent },
    { path: 'employees/:id', component: EmployeeComponent },
    ...
];
```

{% endcode %}

## `<router-outlet>`

The router Directive to indicate the place where the Angular router will load the Component of the currently selected route.

## `routerLink`

{% hint style="danger" %}
Do not used the `<a href="" />` to load routes, because these routes will be loaded but the page will also be reloaded, thus states will be lost.
{% endhint %}

```html
<!-- Full Path -->
<a routerLink="/employee"></a>
<!-- Relative Path -->
<a routerLink="employee"></a>
<!-- Can be used with Arrays to declare multiple hierarchical paths like '/employee/new' -->
<a [routerLink]="['/employee']"></a>
<a [routerLink]="['/employee', 'new']"></a>
```

It can be used with Absolute paths or Relative paths.

* `Absolute paths` always starts with `/` like `/home`.
* `Relative paths` starts **without the** `/`, and always append the passed url to the current url.
  * You can use `../`, like in directories, to remove go back to upper Url paths.
  * You can use `./`, to stay in the current path.

#### Passing `queryParams` to the `routerLink`

```html
<!-- employee?order=asc -->
<a routerLink="/employee" [queryParams]="{ order: 'asc', ... }"></a>
```

#### Passing `fragments` to the `routerLink`

```html
<!-- employee#loading -->
<a routerLink="/employee" [fragment]="'loading'"></a>
```

## `routerLinkActive`

Used to attach a css class to elements, for instance, when selecting the select tabs.

Without `exact: true`, it will check as contained substrings. So `/` will always be true.

```html
<li routerLinkActive="<cssclass>" [routerLinkActiveOptions]="{exact: true}"></li>
```

## Programatically navigate `router.navigate()`

* `navigate()` method doesn't know your currently route. (*So relative paths don't add up*)
  * If the Url passed is `static`, it can be the literal Url `string`.
  * For `dynamic` path, must use an `Array` of path segments.
    * These segments are added to the current Url.
    * Or to the one provided in `relativeTo` config property.
* Some additional configs that you can pass to `navigate()` include:
  * `replaceUrl`: To avoid that the browser can go back to that route if you hit the back button.
  * `relativeTo`: Used for dynamic paths, the specified `relativeTo` will be the main Url where the paths will be added to.
  * `queryParamsHandling`: How navigate handles `queryParams` and `fragments` values after a navigation.

```typescript
@Component({ ... })
export class Component {
    constructor(private router: Router, private route: ActivatedRoute) {}

    goto() {
        // By default it is always relativeTo root '/'
        this.router.navigate(['/employee', ...]);

        // To tell navigate the current path
        this.router.navigate(['/employee', ...], { relativeTo: this.route });
    }
}
```

#### Passing `queryParams`

```typescript
// employess/5/edit?allowEdit=1
this.router.navigate(["/employee", 5, "edit"], { queryParams: { allowEdit: "1" } });
```

#### Passing `fragments`

```typescript
// employess/5/edit#loading
this.router.navigate(["/employee", 5, "edit"], { fragment: "loading" });
```

#### Preserving `queryParams` and `fragments` - after Navigate with `queryParamsHandling`

* `merge`: Will merge new params with existing queryParams.
* `preserve`: Will just preserve the existent queryParams.

```typescript
this.router.navigate(["edit"], { relativeTo: this.route, queryParamsHandling: "merge" | "preserve" });
```

## Nested Routes

Each child route will need a new `<router-outlet>` to render it's paths Components.

* *For instance, in the* `EmployeesComponent` *you will need to have a* `<router-outlet>` *which will be the place it's child routes will be loaded.*

{% code title="app.routes.ts" %}

```typescript
const appRoutes: Routes = [
    { path: "", component: HomeComponent },
    {
        path: "employees",
        component: EmployeesComponent,
        children: [
            // employees/:id
            { path: ":id", component: EmployeeComponent },
            // employees/:id/edit
            { path: ":id/edit", component: EditEmployeeComponent },
        ],
    },
];
```

{% endcode %}

Now Each parent component will need to have a `<router-outlet>`, which is the place it will load it's children Components.

#### **Nested routes to it's own file**

You can grab the `children` routes and put them in a different `.routes.ts` file for more leaner structure.

{% code title="employee.routes.ts" %}

```typescript
export const employeesRoutes: Routes = [
    // employees/:id
    { path: ":id", component: EmployeeComponent },
    // employees/:id/edit
    { path: ":id/edit", component: EditEmployeeComponent },
];
```

{% endcode %}

{% code title="app.routes.ts" %}

```typescript
const appRoutes: Routes = [
    { path: "", component: HomeComponent },
    {
        path: "employees",
        component: EmployeesComponent,
        children: employeesRoutes,
    },
];
```

{% endcode %}

## Wildcard Routes

To handle routes that shouldn't exist.

* `**` is the wildcard and will catch everything that is not currently specified on the routes.
* The order that you use it is VERY IMPORTANT, always use them as the last route.

```typescript
const appRoutes: Routes = [
    { path: "not-found", component: NotFoundComponent },
    { path: "**", redirectTo: "/not-found" },
];

// Or

const appRoutes: Routes = [{ path: "**", component: NotFoundComponent }];
```

## [Redirects](https://angular.dev/guide/routing/common-router-tasks#setting-up-redirects)

To be able to redirect with urls that may always trigger like `path: ''`, you can use the option `pathMatch: 'full'`

```typescript
{ path: '', redirectTo: '/somewhere-else', pathMatch: 'full' }
```

You can use more complex logic in redirect, returning a `string` or `UrlTree`.

```typescript
{
    path: '',
    redirectTo: ({ queryParams }) => {
        const errorHandler = inject(ErrorHandler);
        const userIdParam = queryParams['userId'];
        if (userIdParam !== undefined) {
            return `/user/${userIdParam}`;
        } else {
            errorHandler.handleError(new Error('Attempted navigation to user page without user ID.'));
            return `/not-found`;
        }
    },
}
```

#### `pathMatch` option

* `prefix`: Will look at the route's path and combine with parent paths if available, and then checks if the Url in the browser STARTS with this path.
* `full`: Will look at the route's path and combine with parent paths if available, and then checks if the FULL Url in the browser is equal to this path.

## Setting the Page Title

**Each page in your application should have a unique title so that they can be identified in the browser history.**

The `Router` sets the document's title using the `title` property.

You can also provide custom title strategies:

* *By providing* `ResolverFn` *functions.*
* *Or by extending the* `TitleStrategy`*, check the docs.*

#### `static` titles

```typescript
const appRoutes: Routes = [
    { path: "", component: HomeComponent, title: "Home" },
    { path: "employees", component: EmployeesComponent, title: "Employees" },
];
```

#### `dynamic` title

You can provide dynamic title values by using `resolver` functions, just like in [`dynamic` data](https://kdongs.gitbook.io/kdocs/angular/broken-reference).

```typescript
// Each `resolveTitle` functions comes from it's Component file
const appRoutes: Routes = [
    { path: "", component: HomeComponent, title: resolveHomeTitle },
    { path: "employees", component: EmployeesComponent, title: resolveEmployeesTitle },
];
```

## Passing `static` and `dynamic` Data to a Route

You can use both methods at the same time.

#### `static` data

You can pass static data to routes by providing them in the `data` property.

```typescript
const appRoutes: Routes = [{ path: "", component: HomeComponent, data: { message: "Hello" } }];
```

#### Access with Signals

You can then access the data inside the Component with `input()`, by having a variable with the same name as the data property.

```typescript
export class HomeComponent {
    message = input.required<string>();
}
```

#### Access with `ActivatedRoute` - with `data` (`Observable`)

`data` Observable will provide values from `static` and `dynamic`.

```typescript
export class HomeComponent implements OnInit {
    message: string;
    private activatedRouteDataSubscription;
    private activatedRoute = inject(ActivatedRoute);

    ngOnInit() {
        this.activatedRouteDataSubscription = this.activatedRoute.data.subscribe((values) => {
            this.message = values.message;
        });
    }

    ngOnDestroy() {
        this.activatedRouteDataSubscription.unsubscribe();
    }
}
```

#### `dynamic` data - with `ResolveFn` functions

You can pass dynamic data to routes with `resolve` property of routes.

The `resolve` accepts an object just like the `static` one, but instead of static values you will provide valid Angular `resolvers`, which are functions.

The resolver will be called for every navigation action on the specific route.

* **BUT by default will NOT be re-executed when `queryParams` changes.**
* To allow re-execution set the `runGuardsAndResolvers` to `always` or `paramsOrQueryParamsChange`.

```typescript
const appRoutes: Routes = [
    {
        path: "",
        component: HomeComponent,
        runGuardsAndResolvers: "always",
        resolve: { message: resolveMessage },
    },
];
```

```typescript
export class HomeComponent {
    // Access the dynamic data with Signals
    message = input.required<string>();
    ...
}

// Can use the snapshot since it is executed every time the Route is active
export const resolveMessage: ResolveFn<string> = (activatedRoute: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) => {
    // You may `inject` services in here
    // ...
    return 'Hello: ' + data; // data is of type string
}
```

#### `dynamic` data - with `Resolve` Classes as Services (Avoid)

{% hint style="danger" %}
This is an older way, prefer the use with Functions. (Above)
{% endhint %}

You can pass dynamic data to routes with services implementing `Resolve` class.

```typescript
@Injectable({
    providedIn: "root",
})
export class MessageResolver implements Resolve<string> {
    resolve(activatedRoute: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
        return "Hello: " + data; // data is of type string
    }
}
```

```typescript
const appRoutes: Routes = [
    {
        path: "",
        component: HomeComponent,
        resolve: { message: MessageResolver },
    },
];
```

```typescript
export class HomeComponent {
    message = input.required<string>();
    ...
}
```

## Fetching Url data - with `input()`

{% hint style="info" %}
Changes in the URL will use still use the same Component's instance.&#x20;

This means that `ngOnInit` will NOT execute again, and changes in `:params` for instance will not be picked.

Retrieving data with Signals will garantee to get the new values.
{% endhint %}

{% hint style="warning" %}
When working Signals you must declare in the `provideRouter` the argument `withComponentInputBinding`.
{% endhint %}

```typescript
export const appConfig = {
    providers: [provideRouter(routes, withComponentInputBinding())],
};
```

#### Access Parent Route Data

With `input()` the Component can always access it's own url data, BUT the Component cannot access by **default** it's parent route url data.

Provide an additional configuration `withRouterConfig()`, with `paramsInheritanceStrategy` property.

*Just consider still using **UNIQUE** names to avoid clashing of param names in the routes.*

```typescript
export const appConfig = {
    providers: [
        provideRouter(
            routes,
            withComponentInputBinding(),
            withRouterConfig({
                paramsInheritanceStrategy: "always",
            })
        ),
    ],
};
```

#### Retrieving param `/:userId`

You can retrieve url parameters by having a variable with the exact same name as declared in the Route.

*The expected type will always be* `string` *since the url is a string.*

```typescript
@Component({ ... })
export class UserComponent {
    // user/:userId
    // You may use `required` since Url param will most likely be required in the URL
    userId = input.required<string>();
}
```

#### Retrieving multiple params `/:userId/:name`

```typescript
@Component({ ... })
export class UserComponent {
    // user/:userId
    userId = input.required<string>();
    name = input.required<string>();
}
```

#### Retriving Url queryParams `?allowEdit=value`

Just like a `/:param`, for queryParams just create a variable with the same name as the queryParam.

```typescript
@Component({ ... })
export class UserComponent {
    // user?order=value
    // For queryParams don't use `required` since usually are opcional values
    order = input<'asc' | 'desc'>();
}
```

#### Retrieving Url fragments `#loading`

{% hint style="danger" %}
Not sure how to retrieve with Signals, but it could be just setting the variable `input()` with the same name as the `#fragment` name.
{% endhint %}

## Fetching Url data - with `ActivatedRoute` service

With `Observables` a Component can by default access it's own route params AND parent route params.

{% hint style="info" %}
Changes in the URL will use still use the same Component's instance.

This means that `ngOnInit` will NOT execute again, and changes in `:params` for instance will not be picked.

Retrieving data with Observables will garantee to get the new values.

Retriving data with `snapshot` will NOT get new values.
{% endhint %}

#### Fetching data the Reactive way - with (`Observable`)

**Retrieving Url params** `/:userId` **- with** `paramMap`

You can access url params by subscribing to `paramMap` property of `ActivatedRoute`.

```typescript
@Component({ ... })
export class UserComponent implements OnInint, OnDestroy {
    userId: string;
    private activatedRouteParamsSubscription;

    constructor(private activatedRouteService: ActivatedRoute) {}
    // or
    readonly activatedRouteService = inject(ActivatedRoute);

    ngOnInit() {
        this.activatedRouteParamsSubscription = this.activatedRouteService.paramMap.subscribe({
            next: (paramMap) => {
                this.userId = paramMap.userId;
            }
        });
    }

    ngOnDestroy() {
        this.activatedRouteParamsSubscription.unsubscribe();
    }
}
```

**Retrieving Url multiple params** `/:userId/:name` **- with** `paramMap`

```typescript
this.activatedRouteParamsSubscription = this.activatedRouteService.paramMap.subscribe({
    next: (paramMap) => {
        this.userId = paramMap.userId;
        this.name = paramMap.name;
    },
});
```

**Retrieving Url queryParams** `?allowEdit=value` **- with** `queryParams`

You can access url queryParams by subscribing to `queryParams` property of `ActivatedRoute`.

```typescript
this.activatedRouteQueryParamsSubscription = this.activatedRouteService.queryParams.subscribe({
    next: (params) => {
        this.order = params.order;
    },
});
```

**Retrieving Url fragments** `#loading` **- with** `fragment`

You can access url fragment by subscribing to `fragment` property of `ActivatedRoute`.

```typescript
this.activatedRouteFragmentSubscription = this.activatedRouteService.fragment.subscribe({
    next: (fragment) => {
        this.loading = fragment;
    },
});
```

#### Fetching data NOT Reactive - with (`.snapshot`)

Values from the `snapshot` are not Observables, they are actual values to be accessed.

The snapshot values are **NOT reactive** to the Url changes. *(The values displayed will be the values of the FIRST run on the Component)*

**Retrieving Url param** `/:userId` **- with** `snapshot.paramMap` **or** `snapshot.params`

```typescript
@Component({ ... })
export class UserComponent implements OnInint, OnDestroy {
    userId: string;

    constructor(private activatedRouteService: ActivatedRoute) {}
    // Or
    readonly activatedRouteService = inject(ActivatedRoute);

    ngOnInit() {
        this.userId =  this.activatedRouteService.snapshot.paramMap.userId;
    }
}
```

#### Retrieving Url queryParams `?allowEdit=value` - with `snapshot.queryParamMap` or `snapshot.queryParams`

```typescript
@Component({ ... })
export class UserComponent implements OnInint {
    allowEdit: string;

    constructor(private route: ActivatedRoute) {}

    ngOnInit() {
        this.allowEdit = this.route.snapshot.queryParams['allowEdit'];
    }
}
```

#### Retrieving Url fragments `#loading` - with `snapshot.frament`

Only one fragment may be passed.

```typescript
@Component({ ... })
export class UserComponent implements OnInint {
    loading: string;

    constructor(private route: ActivatedRoute) {}

    ngOnInit() {
        this.loading = this.route.snapshot.loading;
    }
}
```

## Route Guards

### [Generating Guard (CLI)](https://angular.dev/cli/generate/guard)

```bash
ng g guard [guard-name] [options]
```

It is code that can be executed before a route is loaded or when leaving a route, that checks whether a certain navigation action should be permitted or not.

Typically guards will have their own files `<guard-name>.guard.ts`.

*Useful for preventing unauthorized access.*

*Add Guards to routes by using the routes properties that start with `can`.*

By default a Guard will guard the Route where it was declared, and it's child routes.

Guards can be defined by:

* Guard functions.
* Guard Services that implement certain interfaces *(This is the old way)*

```typescript
const appRoutes: Routes = [
    {
        path: "",
        component: HomeComponent,
        // They receive an array of Guards (Functions or Classes)
        canMatch: [],
        canActivate: [],
        canActivateChild: [],
        canDeactivate: [],
    },
];
```

### `canMatch`

Allows you to control whether the entire route should be matched by a certain navigation action or not.

For instance, if some path that has been entered into the Url should match this route or not.

#### with Functions

```typescript
/**
 * route: Current route that is trying to be matched.
 * segments: An array of Url path segments.
 */
export const authGuard: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
    // Return `true` to grant access to the route, `false` to deny it
    return true;

    // Or you may return a UrlTree to redirect the navigation
    return router.parseUrl("/home");

    // Or you can redirect like this
    return new RedirectCommand(router.parseUrl("/home"));
};
```

#### with Classes

```typescript
export class AuthGuard implements CanMatch {
    canMatch(route: Route, segments: UrlSegment[]) {
        return true;
    }
}
```

### `canActivate`

Comes one step after `canMatch` but before the component has been loaded.

It will be checked by Angular once a route has been identified as matching for the currently active path.

```typescript
export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
    // Return `true` to grant access to the route, `false` to deny it
    return true;

    // Or you may return a UrlTree to redirect the navigation
    return router.parseUrl("/home");

    // Or you can redirect like this
    return new RedirectCommand(router.parseUrl("/home"));
};
```

### `canActivateChild`

* Can be used if you want to activate the route, the Component, but not necessarily the child components of that route.

```typescript
export const authGuard: CanActivateChildFn = (childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
    // Return `true` to grant access to the route, `false` to deny it
    return true;

    // Or you may return a UrlTree to redirect the navigation
    return router.parseUrl("/home");

    // Or you can redirect like this
    return new RedirectCommand(router.parseUrl("/home"));
};
```

### `canDeactivate`

To control whether a user is allowed to leave a page or not.

```typescript
export class HomeComponent {
    unsavedData = signal<boolean>(false);
}
```

```typescript
// Specify `HomeComponent` as the implicit type of `CanDeactivateFn` so that you receive it in the `component` parameter
export const canLeavePage: CanDeactivateFn<HomeComponent> = (component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) => {
    // Return `true` to let navigate out of the component, or `false` to deny it
    if (component.unsavedData() === false) return false;
    return true;

    // Making a confirmation dialog
    if (component.unsavedData() === false) {
        return window.confirm("Do you really want to leave?");
    }
    return true;

    // You may also return a UrlTree to redirect the navigation
    return router.parseUrl("/home");
};
```
