---
# System prepended metadata

title: Routes

---

Routes
=
> app.module.ts
```typescript=
import { Routes } from '@angular/router';

// declare routes
const appRoutes: Routes =[
    { path: '', component: HomeComponent }
    { path: 'users', component: UsersComponent }
    { path: 'servers', component: ServersComponent }
    //in case we want the opening page to redirect to servers - we will do:
    { path: '', redirectTo: '/servers', pathMatch: 'full' }
    // pathMatch must be added or else this cause an error:
    // because '' matches every route
]

// register the routes to our angular app
imports: [
    RouterModule.forRoot(appRoutes)
]
```
when `localhost:4200/users` reached, `UserComponent` is called
this register the routes to our angular app

> template.html
```htmlmixed=
<li role="presentation">
    <a
        routerLink="/"
        routerLinkActive="active"
        [routerLinkActiveOptions]="{exact: true}"
        > Home </a> </li>
<li role="presentation">
    <a
        routerLink="/users"
        routerLinkActive="active"
        > Users </a> </li>
<li role="presentation">
    <a
        [routerLink]="['/users','10','Anna']" // `/users/10/Anna`
        routerLinkActive="active"
        > Users </a> </li>
<router-outlet> </router-outlet>
```
`routerLink` - Avoid reloading on each route activation
`routerLinkActive` - Mark as active only when contains this path
`routerLinkActiveOptions` - only at this exact path `/` mark as active

Trigger route from within our code - load a route automatically on button click
-
1. Inject router into the component
2. Call a function from the button
```htmlmixed=
<button class="btn btn-primary" (click)="onLoadServers">
     Load Servers
</button>
```

```typescript=
import { Router, ActivatedRoute } from `@angular/router`;

constructor(
            private router: Router
            private route: ActivatedRoute) { }

onLoadServers() {
    this.router.navigate(['/servers']);
    // absolute path. navigate takes an array of elements in the path
    
    this.router.navigate(['servers'], {relativeTo: this.route});
    // let navigate know on what route it is currently in
    // reload the page
    // results with /servers/servers
}
```
Passing parameter to Routes
-
> app.module

```typescript=
    { path: 'users/:id/:name', component: UsersComponent }
    // dynamic id, name
```
> app.component.ts
```typescript=
ngOnInit() {
    this.user = {
        id: this.route.snapshot.params[id],
        name: this.route.snapshot.params[name]
        // retrieve id and name into component dynamically from route path
    }
    // Or with + to convert string from route to number
    const id =+this.route.snapshot.params['id'];
    this.user = this.UserService.getUser(id);
    
    // observer - deal with asynchronous events and
    // be fired whenever new data is sent to this route
    // not executed on ngOnInit
    this.route.params.subscribe(
        (params: Params) => {
            this.user.id = params['id'];
            this.user.name = params['name'];
        }
    );
}
```
* If we add our own observer --> we need to unsubscribe via onDistroy (on course)

Query parameters, Fragment (Pass & retrieve)
-
`localhost:4200/users/1/Max?mode=editing#loading`
Route to an edit page and decide if allowed
### Pass
```typescript
    { path: 'servers/:id/edit', component: EditServerComponent }
```
```htmlmixed
<a
    [routerLink]="['/servers', server.id,'edit']"
    [queryParams]="{allowEdit: '1'}"
    fragment="loading"
    routerLinkActive="active"
    > Edit Server </a> </li>
```
### Retrieve
> app.component.ts
>
```typescript
this.route.snapshot.queryParams;
this.route.queryParams.subscribe();
```
### Nested Router
load menu next to current menu
```typescript=
const appRoutes: Routes =[
    { path: 'servers', component: ServersComponent, children: [
        { path: '/:id', component: ServersComponent }
        { path: '/:id/edit', component: EditServersComponent }
    ] }
]
```
```htmlmixed=
<router-outlet></router-outlet>
// instead of app-servers, app-servers-edit components
```
Wildcard Route
-
For Routes we don't have - 404/Redirect page
```typescript=
{ path: 'page-not-found', component: PageNotFoundComponent }
{ path: '**', redirectTo: '/page-not-found' }
```
`**` always put last in Routes

Cleaner code
-
Creatring a router mdoule and remove routing from app.module
> app.module
```typescript=
import { AppRoutingModule } from './app-routing.module.ts';
imports: [
    AppRoutingModule
]
```
> app-routing.module.ts
```typescript=
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import {HomeComponent } from '/home/home.component';
// import all Components
 
const appRoutes: Routes =[
    { path: '', component: HomeComponent }
    { path: 'users', component: UsersComponent }
    { path: 'servers', component: ServersComponent }
]

@NgModule({
    imports: [
        RouterModule.forRoot(appRoutes)
    ],
    exports: [RouterModule]
})
export class AppRoutingModule {
    
}
```

Guards
-
Enables access to routes only for logined users
> auth-guard.service.ts
(dont forget importing to `app.module.ts`)
```typescript=
import {
    CanActivate,
    ActivatedRouterSnapshot,
    RouterStateSanpshot } from `@angular/core`;
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
    constructor(
        private authService: AuthService
        private router: Router) {}
    
    canActivate(route: ActivateRouteSnapshot,
                state: RouterStateSnapshot):
                    Observable<boolean> | Promise<boolean> | boolean {
        return his.authService.isAuthenticated().then(
            (authenticated: boolen) => {
                if (authenticated)
                    return true;
                else
                    this.router.navigate(['/']);
                    // navigate home or just return false
            });
    }
    
    // To guard only the childs of this route
    canActivateChild(route: ActivateRouteSnapshot,
                    state: RouterStateSnapshot):
                    Observable<boolean> | Promise<boolean> | boolean {
        return this.canActivate(route,state);
}
```
> auth.service.ts (dummy service - in realworld, here we access the server)
```typescript=
export class AuthService {
    loggedIn = false;

    isAuthenticated() {
        const promise = new Promise(
            (resolve, reject) => {
                setTimeout( () => {
                    resolve(this.loggedIn);
                }, 800); //ms
            }
        );
        return promise;
    }    

    login(){
        this.loggedIn = true;
    }

    logout(){
        this.loggedIn = false;
    }
}
```
> app-routing.module.ts
```typescript=
// Inside the array, enter all guards should be used on this route
{ path: 'users', canActivate: [AuthGuard], component: UsersComponent }
// Guard childs, add this in parent
{ path: 'users', canActivateChild: [AuthGuard], component: UsersComponent }

```

Enable Login with AuthService
-
> home.component.html
```htmlmixed=
<button class="btn btn-default" (click)="onLogin()">
    Login
</button>
<button class="btn btn-default" (click)="onLogout()">
    Logout
</button>
```
> home.component.ts
```typescript=
constructor(... private authService: AuthService) { }

onLogin() {
    this.authService.login();
}

onLogout() {
    this.authService.logout();
}
```
no visual indication yet, but content is now available only for logged in users.

Control wheter you're allowed to leave a Route 
-
like leaving without saving/submitting.
> edit.server.ts
```typescript=
import { Observeable } from 'rxjs/Observable';

export class EditServerComponent implements CanComponentDeactivate {
    allowEdit = false;
    changesSaved = false;

    onUpdateServer(){
        this.changesSaved = true;
    }

    canDeactivate() {
        if (!this.allowEdit)
            return true;
        // onInit we binded serverName with server.name
        // serverName: bound property to input
        // server.name: previous value
        if (this.serverName !== this.server.name ||
            this.serverStatus !== this.server.status &&
           !this.changesSaved)
            return confirm('Do you want to discard changes?');
        else
            return true;
    }
}
   
```
**Add a new File**
> can-deactivate-guard.service.ts
```typescript=
export interface CanComponentDeactivate {
    canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
//interface, therefore needs to be implmeneted.
export class CanDeactivateGuard implements
    CanDeacivate<CanComponentDeactivate>{
    
        canDeactivate(component: CanComponentDeactivate,
                      currentRoute: ActivatedRouteSnapshot,
                      currentState: RouterStateSnapshot,
                      nextState?: RouterStateSnapshot):
                    // nextState? - Optional argument
            // Function's returned value is the below
            Observable<boolean> | Promise<boolean> | boolean {
        
        return component.canDeactivate();
    }
}
```
> app-routing.module.ts
```typescript=
{ path: 'users', component: UsersComponent,
    canDeactivate: [CanDeactivateGuard]}
```
and don't forget to provide service in `app.module.ts`

Get dynamic data to a Route
-
(Previeus chapter deals with static data)
* Resolver (service) - run some code before the Route is rendered
* Preload some data from the server before accesing/displaying route
* Alternative - Load instantly and present a spinner. 
* Create a new service to retrieve data with a resolver instead of via params.
* Important when using asynchronis data

> server-resolver.service.ts
```typescript=
import { Resolve } from '@angular/router';

interface Server {
    id: number;
    name: string;
    status: string;
}

@Injectable
export class ServerResolver implements Resolve<Server> {
    constructor(private serversService: ServersService)
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
        Observable<Server> | Promise<Server> | Server {
            //'+' - cast to integer
            return this.serversService.getServer(+route.params['id'])
    }
}
```
> app-routing.module.ts
```typescript=
{ path: 'users', component: UsersComponent,
    resolve: {server: ServerResolver} }
```
this will map the data it gives us back
> server.component.ts
```typescript=
ngOnInit() {
    this.route.data.subscribe(
        (data: Data) => {
            this.server = data['server']
        }
    );
}
```
Hash Configuration
-
On real server, the URL is parsed by the web server first, not by angular.
Adding `#` to the URL - `localhost:4200/#/servers`
informs the server to parse only the first part of the URL `localhost:4200/`
and let Angular parse the rest `/servers`
> app-routing.module.ts
```typescript=
imports: [
    RouterModule.forRoot(appRoutes, {useHash: true})
]
```
<!-- <hr> -->
### Avoid reloading the page when clicking on elements
1. Remove `href="#"` - this calls the web server
2. Add
```CSS
style="cursor: pointer;"
```
to view clickable when hovering over the element.
### New item / Edit item page
> recipe-edit.component.ts
```typescript=
export class RecipeEditComponent implements OnInit {
  id: number;
  editMode = false;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params
      .subscribe(
        (params: Params) => {
            // retrieve id dynamically
          this.id = +params['id'];
            // if route has id parameter - then we are in Edit mode
            // else: we are creating a New item
          this.editMode = params['id'] != null;
        }
      );
  }
}
```
> app-routing.module.ts
```typescript=
{ path: 'new', component: RecipeEditComponent },
{ path: ':id', component: RecipeDetailComponent },
```
* Must put `new` before `:id` to avoid error - Routing does first match prefix
(`localhost:4200/recipe/new` will fail because `id=new` not identified as valid id)
* Both `New` and `Edit` are handled within same component - can be tell apart by `editMode` property.