Поиск по сайту:

Как использовать преобразователи маршрутов с Angular Router


Введение

Один из способов обработки получения и отображения данных из API — направить пользователя к компоненту, а затем в хуке ngOnInit этого компонента вызвать метод службы для получения необходимых данных. При получении данных, возможно, компонент может показывать индикатор загрузки.

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

Одним из доступных для использования API является Hacker News API. Hacker News — это сайт для обмена ссылками и их обсуждения. API можно использовать для получения наиболее популярных сообщений и отображения информации об отдельных сообщениях.

В этом руководстве вы реализуете преобразователь маршрутов, который получает данные из Hacker News API перед переходом к маршруту, отображающему собранные данные.

Предпосылки

Для выполнения этого урока вам понадобятся:

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

Это руководство было проверено с помощью Node v15.3.0, npm v6.14.9, @angular/core v11.0.1, @angular/common v11. 0.1, @angular/router версии 11.0.1 и rxjs версии 6.6.0.

Шаг 1 — Настройка проекта

Для целей этого руководства вы создадите проект Angular по умолчанию, сгенерированный с помощью @angular/cli.

  1. npx @angular/cli new angular-route-resolvers-example --style=css --routing --skip-tests

Это настроит новый проект Angular со стилями, установленными на «CSS» (в отличие от «Sass», «Less» или «Stylus»), с включенной маршрутизацией и пропуском тестов.

Перейдите в только что созданный каталог проекта:

  1. cd angular-route-resolvers-example

На данный момент у вас есть новый проект Angular с @angular/router.

Шаг 2 — Создание резольвера

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

Во-первых, создайте отдельный класс для распознавателя в собственном файле:

  1. ./node_modules/@angular/cli/bin/ng generate resolver news

Это будет использовать @angular/cli для создания преобразователя с именем news:

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

import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class NewsResolver implements Resolve<Observable<string>> {
  resolve(): Observable<string> {
    return of('Route!').pipe(delay(2000));
  }
}

Для реализации интерфейса Resolve маршрутизатора Angular требуется, чтобы класс имел метод resolve. Все, что возвращается из этого метода, будет разрешенными данными.

Этот код вернет наблюдаемую, которая переносит строку после задержки в 2 секунды.

Шаг 3 — Настройка маршрутов

Чтобы испытать два разных маршрута, вам понадобятся два новых компонента. home будет целевой страницей. А в top будут представлены самые популярные сообщения из Hacker News API.

Сначала используйте @angular/cli для создания компонента home:

  1. ./node_modules/@angular/cli/bin/ng generate component home

Затем используйте @angular/cli для создания компонента top:

  1. ./node_modules/@angular/cli/bin/ng generate component top

Теперь вы можете настроить модуль маршрутизации для включения резолвера.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { NewsResolver } from './news.resolver';

import { TopComponent } from './top/top.component';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
  {
    path: '',
    pathMatch: 'full',
    component: HomeComponent
  },
  {
    path: 'top',
    component: TopComponent,
    resolve: { message: NewsResolver }
  }
];

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

Обратите внимание, что преобразователь предоставляется точно так же, как служба, а затем вы включаете преобразователь в определение маршрута. Здесь разрешенные данные будут доступны по клавише message.

Шаг 4 — Доступ к разрешенным данным в компоненте

В компоненте вы можете получить доступ к разрешенным данным, используя свойство data объекта ActivatedRoute snapshot:

import { Component, OnInit } from '@angular/core';

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

@Component({ ... })
export class TopComponent implements OnInit {
  data: any;

  constructor(private route: ActivatedRoute) {}

  ngOnInit(): void {
    this.data = this.route.snapshot.data;
  }
}

Теперь в компоненте вы можете получить доступ к сообщению Route! следующим образом:

<p>The message: {{ data.message }}</p>

На этом этапе вы можете скомпилировать ваше приложение:

  1. npm start

И посетите localhost:4200/top в веб-браузере.

Output
The message: Route!

При переходе к маршруту top вы заметите, что теперь есть 2-секундная задержка, потому что данные обрабатываются первыми.

Шаг 5 — Разрешение данных из API

Давайте сделаем вещи более реальными, фактически получив некоторые данные из API. Здесь вы создадите службу, которая получает данные из Hacker News API.

Вам понадобится HttpClient для запроса конечной точки.

Сначала добавьте HttpClientModule в app.module.ts:


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Затем создайте новую службу:

  1. ./node_modules/@angular/cli/bin/ng generate service news

Это будет использовать @angular/cli для создания службы с именем news:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class NewsService {
  constructor(private http: HttpClient) { }

  getTopPosts() {
    const endpoint = 'https://hacker-news.firebaseio.com/v0/topstories.json';

    return this.http.get(endpoint);
  }
}

И теперь вы можете заменить строковый код в NewsResolver на NewsService:

import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs';

import { NewsService } from './news.service';

export class NewsResolver implements Resolve<any> {
  constructor(private newsService: NewsService) {}

  resolve(): Observable<any> {
    return this.newsService.getTopPosts();
  }
}

На этом этапе, если вы посмотрите на маршрут top в браузере, вам будет представлен список чисел, представляющих id самых популярных сообщений в Hacker News.

Шаг 6 — Доступ к параметрам маршрута

Вы можете получить доступ к текущим параметрам маршрута в вашем преобразователе с помощью объекта ActivatedRouteSnapshot.

Вот пример, где вы могли бы использовать преобразователь, чтобы получить доступ к параметру id текущего маршрута.

Сначала используйте @angular/cli для создания преобразователя с именем post:

  1. ./node_modules/@angular/cli/bin/ng generate resolver news

Затем измените post.resolver.ts, чтобы использовать ActivatedRouteSnapshot:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

import { NewsService } from './news.service';

@Injectable({
  providedIn: 'root'
})
export class PostResolver implements Resolve<any> {
  constructor(private newsService: NewsService) {}

  resolve(route: ActivatedRouteSnapshot): Observable<any> {
    return this.newsService.getPost(route.paramMap.get('id'));
  }
}

Затем добавьте метод getPost в NewsService:

// ...

export class NewsService {
  constructor(private http: HttpClient) { }

  // ...

  getPost(postId: string) {
    const endpoint = 'https://hacker-news.firebaseio.com/v0/item';

    return this.http.get(`${endpoint}/${postId}.json`);
  }
}

И добавьте PostResolver и маршрут post/:id в app-routing.module.ts:

// ...

import { PostResolver } from './post.resolver';

// ...

const routes: Routes = [
  // ...
  {
    path: 'post/:id',
    component: PostComponent,
    resolve: { newsData: PostResolver }
  }
];

// ...

Затем создайте новый PostComponent:

  1. ./node_modules/@angular/cli/bin/ng generate component post

Затем измените post.component.ts, чтобы использовать данные снимка:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({ ... })
export class PostComponent implements OnInit {
  data: any;

  constructor(private route: ActivatedRoute) { }

  ngOnInit(): void {
    this.data = this.route.snapshot.data;
  }
}

И измените post.component.html, чтобы отобразить title:

<p>{{ data.newsData.title }}</p>

Теперь, если пользователь переходит на http://localhost:4200/post/15392112, данные для идентификатора сообщения 15392112 будут разрешены.

Шаг 7 — Обработка ошибок

В случае возникновения ошибки при извлечении данных вы можете отловить и устранить ошибку в распознавателе с помощью оператора catch RxJS. Что-то вроде этого, например:

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

import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { NewsService } from './news.service';

@Injectable()
export class NewsResolver implements Resolve<any> {
  constructor(private newsService: NewsService) {}

  resolve(): Observable<any> {
    return this.newsService.getTopPosts().pipe(catchError(() => {
      return of('data not available at this time');
    }));
  }
}

Или вы можете вернуть наблюдаемый объект EMPTY и вернуть пользователя к корневому пути:

import { Injectable } from '@angular/core';
import { Router, Resolve } from '@angular/router';

import { Observable, EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { NewsService } from './news.service';

@Injectable()
export class NewsResolver implements Resolve<any> {
  constructor(private router: Router, private newsService: NewsService) {}

  resolve(): Observable<any> {
    return this.newsService.getTopPosts().pipe(catchError(() => {
      this.router.navigate(['/']);
      return EMPTY;
    }));
  }
}

Эти два подхода улучшат взаимодействие с пользователем в случае возникновения ошибки при получении данных из API.

Заключение

В этом руководстве вы реализовали преобразователь маршрутов, который получает данные из Hacker News API перед переходом к маршруту, отображающему собранные данные. Это было достигнуто за счет использования @angular/router, @angular/common/http и rxjs.

Если вы хотите узнать больше об Angular, ознакомьтесь с нашей темой по Angular, где вы найдете упражнения и проекты по программированию.