Angular: роутинг, програмный роутинг, ссылки

Пример простого роутинга

Файл app-routing.module.ts и импортирование модулей происходит автоматически, если при установке нового приложения подтвердить наличие роутинга. Вот так выглядит простой роутинг в файле app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { InventoryComponent } from './inventory/inventory.component';
import { LocationComponent } from './location/location.component';
import { MoveDetalisComponent } from './move-detalis/move-detalis.component';

const routes: Routes = [
  { path: '', component: InventoryComponent },
  { path: 'location', component: LocationComponent },
  { path: 'move-detalis', component: MoveDetalisComponent, children: [
    { path: '', component: ListDetalisComponent },
    { path: 'foo', component: FaqDetalisComponent},
    { path: ':id', component: DetalComponent },
    ] },
  { path: '404', component: ErrorPageComponent },
   { path: '**', redirectTo: '/404' }, //все несуществующие пути редиректим на 404
];

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

Вывод роутинга внутри коревого компонента (app.component.html) либо родительского компонента, если он имеет дочерных: <router-outlet></router-outlet>

Пример:

<div class="some-class">
<app-header></app-header>
<router-outlet></router-outlet>
</div>

Ссылки и их настройка для роутинга:

Пример ссылок:

<a routerLink="/about">About</a>
<a [routerLink]=['/users']">Users</a>

Пример, как присваивать нужные стили активной ссылке:

<li routerLinkActive="active-style"
    [routerLinkActiveOptions]="{exact: true}">
<a routerLink="/">Home</a></li>
<li routerLinkActive="active-style"><a routerLink="/about">Home</a></li>
<li routerLinkActive="active-style"><a routerLink="/users">Home</a></li>

Обратите внимание, что для ссылки только со слешем (главная страница) пришлось прописать опцию exact: true, которая делает ее активной только при точном совпадении. Иначе все ссылки, которые начинаются с такого же начала получаются активными.

Динамические ссылки для списка “итерурумых компонент”, выведенного при помощи *ng-if:

Например, мы хотим вывести список элементов из массива, при клике на которые они загрузятся с параметром в url.

<app-component-item
  *ngFor="let itemtEl or itemsList; let idx = index"
   [itemData]=[itemtEl]
   [index]="i"
>
</app-component-item>

Теперь в компоненте app-component-item просто добавим ссылки при выводе:

<a [routerLink]="[index]"> .... </a>

Програмный роутинг (вызывается внутри класса, а не шаблона):

Относительно корня сайта:

import {Router} from '@angular/router"
...
constructor(private router: Router) {}

onLoadAction() {
  this.router.navigate(['/profile'])
}

Относительно текущего роута:

import {Router} from '@angular/router"
...
constructor(private router: Router) {}

onLoadAction() {
  this.router.navigate(['edit'], {relativeTo: this.route})
}

Смысл такого роутинга (относительно текущего роута) заключается в перемещениях, когда в данный момент мы уже пребываем на динамическом каком-то роуте, например, содержащем id в своем пути.

Вот пример более сложного вариант относительно текущего роута, который приведет туда же, куда верхний более простой:

this.router.navigate([''../', this.id, 'edit'], {relativeTo: this.route})

Получение параметров и других данных из объекта в роуте, включая id

Всего есть 2 способа получить параметры из роута(урла): статический и динамический (названия способов выдуманные).

1) Статический

Это когда мы просто внутри компонента (того что отображается при роуте с данным параметром) получаем параметр. Например:

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

....
  ngOnInit(): void {
    const id: any = this.route.snapshot.paramMap.get('id');
    console.log(id);
  }

2) Динамический

import { ActivatedRoute, Params } from '@angular/router';

...

  ngOnInit(): void {
    this.route.params.subscribe((params: Params) => {
      this.id = +params['id'];
      this.fooServices.testGetIdFromComponent(this.id);
    });
  }

...

При такой записи мы с сервиса fooServices.service.ts теперь можем получить нужный нам элемент по id:

testGetIdFromComponent(id: number) {
  return this.dataList[id];
}

Проверка на edit mode

Пример проверки на edit mode компонента (если true, то отрисовывается компонент с инпут полями для редактирования) с использованием роутов.

Например, site.ru/users/0 – режим будет false, так как есть id parametr, типизированный как число. А site.ru/users/0/edit либо site.ru/users/new будут false:

Проверка edit mode в angular

Защита роутов (без авторизации и т.п.)

Например, нужно сделать, чтобы на некоторые роуты нельзя было зайти без авторизации.

1. Сервис для авторизации (имитация сервера)

Создаем простой сервис /app/auth.service.ts для эмуляции авторизации с возвращением промиса при попытке получить значение.

/app/auth.service.ts:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private isAuth: boolean = false;

  login() {
    this.isAuth = true;
  }

  logout() {
    this.isAuth = false;
  }

  isAuthenticated(): Promise<boolean> {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(this.isAuth);
      }, 1000);
    });
  }

  constructor() {}
}

2. Гвард для проверки авторизации

1) Создаем файл в папке /app. Например, auth.guard.ts. Создаем в нем класс AuthGuard и имплементируемся от интерфейса CanActivate.

2) Создаем метод canActivate, который принимает параметры route и state, что видно в примере ниже.

Возвращаемое значение можно указать разным – Observablem или Promise по типу <boolean> либо просто boolean (any в примере, чтобы не было ошибки из-за предполагаемого TS underfined).

/app/auth.guard.ts:

import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | any {
    return this.authService.isAuthenticated().then((isAuth): boolean | void => {
      if (isAuth) {
        return true;
      } else {
        this.router.navigate(['/'], {
          queryParams: {
            auth: false,
          },
        });
      }
    });
  }
}

3. Настраиваем роутеры, которые надо защитить

В файле с массивом роутов для каждого роута, который надо защитить, настраиваем поле canActivate, куда передаем массив гвардов (у нас он один).

app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { InventoryComponent } from './inventory/inventory.component';
import { LocationComponent } from './location/location.component';
import { MoveDetalisComponent } from './move-detalis/move-detalis.component';

const routes: Routes = [
  { path: '', component: LocationComponent },
  { path: 'location', component: InventoryComponent, canActivate: [AuthGuard] }, <---- здесь защитили
  { path: 'move-detalis', component: MoveDetalisComponent, children: [{ path: ':id', component: LocationComponent }] },
];

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

Защита дочерных компонент

1. Для этого в том же гварде (/app/auth.guard.ts) ниже создаем еще один метод под названием canActivateChild:

/app/auth.guard.ts:

...
 canActivateChild(
   route: ActivatedRouteSnapshot, 
   state: RouterStateSnapshot
   ): Observable<boolean> | Promise<boolean> | boolean {
    return this.canActivate(route, state);
  }
...

2. Прописываем в файле с роутами для компонента, у которого есть дочерные, ограничение canActivateChild (не canActivate, как для родителя):

app-routing.module.ts:

...
const routes: Routes = [
  { path: '', component: LocationComponent },
  { path: 'location', component: InventoryComponent, canActivate: [AuthGuard] },
  {
    path: 'move-detalis',
    component: MoveDetalisComponent,
    canActivateChild: [AuthGuard], // <--- здесь 
    children: [{ path: ':id', component: LocationComponent }],
  },
];
...

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *