DevGang
Авторизоваться

Основы Angular: введение в Angular Interceptors

Перехватчики могут помочь обеспечить отличный пользовательский интерфейс в вашем приложении Angular для HTTP-запросов.

Мы поиграем с одной из мощных функций Angular — interceptor, которые помогут нам упростить работу с HTTP-запросами и ответами.

Это помогает поместить в одном месте управление всеми вызовами API; сегодня мы увидим, когда они полезны и как их создавать и максимизировать их силу.

Что такое Interceptor?

Если вы раньше работали со службой в Angular, interceptor (перехватчики) покажутся вам знакомыми, потому что они представляют собой службу Angular с реализацией интерфейса HttpInterceptor.

Перехватчики работают между нашим приложением и сервером и взаимодействуют с запросом и ответом.

Сила перехватчиков заключается в том, как они упрощают все запросы в нашем приложении, а не вносят изменения в каждое место, где мы делаем HTTP-вызовы.

Зачем использовать Interceptor?

Перехватчики помогают нам обеспечить обработку всех HTTP-запросов и ответов перед отправкой или получением запроса, что дает нам возможность управлять связью.

У нас есть несколько мест или сценариев их использования:

  • Ведение журнала и отчетность о прогрессе
  • Добавление заголовков к запросу
  • Кэширование на стороне клиента

Далее начинаем использовать interceptor.

Мы покажем список игроков и команд NBA, сделавших два HTTP-запроса, отобразим загрузку и скроем ее, когда сервер ответит.

Одно решение показывает экран загрузки в каждом методе для HTTP-вызовов. Что произойдет, если нам нужно будет добавить больше запросов в будущем с тем же поведением? По этой причине использование перехватчика является лучшим подходом.

Service

Мы создадим в приложении две службы: nba.service.ts и loader.service.ts.

NbaService имеет методы getPlayers и getTeams для связи с API и возврата данных.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, Observable } from 'rxjs';

@Injectable({ provided: 'root' })
export class NbAService {

  constructor(private httpClient: HttpClient) {

  }

  getPlayers(): Observable<any> {
    return this.httpClient.get('https://www.balldontlie.io/api/v1/players').pipe(
      map((response: any) => {
        return response.data;
      })
    );
  }

  getTeams(): Observable<any> {
    return this.httpClient.get('https://www.balldontlie.io/api/v1/teams').pipe(
      map((response: any) => {
        return response.data;
      })
    );
  }

}

Служба loader предоставляет наблюдаемое свойство isLoading$ для получения состояния загрузчика, а методы show и hide - для изменения его состояния.

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

@Injectable({
  providedIn: 'root'
})
export class LoaderService {
  isLoading$ = new Subject<boolean>();

  show(): void {
    this.isLoading$.next(true);
  }
  hide(): void {
    this.isLoading$.next(false);
  }
}

У нас есть услуги для приложения. Следующим шагом будет создание Interceptor.

Создание Interceptor

Interceptor очень похожи на службы, использующие декоратор @Injectable(), но вместо этого реализуют интерфейс HttpInterceptor. Мы должны реализовать метод перехвата с двумя параметрами: req для принятия запроса и next для перехода к следующему обработчику.

Создайте новый класс с декоратором @Injectable(), реализуйте интерфейс HttpInterceptor и внедрите службу загрузчика в конструктор.

В методе intercept мы вызываем loader.show, чтобы установить для свойства isLoading значение true. Параметр next — это наблюдаемый httpRequest, поэтому, используя оператор finalize из rxjs, когда он завершится, вызовите метод hide.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { finalize, Observable } from 'rxjs';
import { LoaderService } from '../services/loader.service';

@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
  constructor(private loader: LoaderService) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.loader.show();
    return next.handle(req).pipe(
      finalize(() => {
        this.loader.hide();
      }));
  }
}

HTTP-запрос возвращается с сервера к клиенту, Interceptor устанавливает для свойства загрузки значение true, а по завершении — значение false. Позже мы используем свойство isLoading в компоненте, чтобы скрыть или показать экран загрузки.

Регистрация Interceptor

Перейдите к вашему app.module.ts и импортируйте модуль HttpClient для HTTP-запроса в область импорта.

Добавьте Interceptor в раздел провайдера и импортируйте HTTP_INTERCEPTORS с параметром useClass, чтобы назначить LoaderInterceptor.

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { LoaderInterceptor } from './interceptors/loader.interceptor';

@NgModule({
  declarations: [
    AppComponent

  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Демонстрация экрана загрузки

Чтобы использовать возможности Interceptor и сервисов, нам нужно импортировать его туда, где это необходимо, и принести из него сервисы через конструктор компонента.

Мы добавляем три свойства — player и team для хранения данных из ответа API и свойство loading$ для получения значения от loaderService.

Далее нам нужно вызвать методы из сервисов с помощью двух кнопок в пользовательском интерфейсе для вызова методов loadPlayers и LoadTeams.

В файле TypeScript код выглядит так:

import { Component } from '@angular/core';
import { LoaderService } from './services/loader.service';
import { NbAService } from './services/NBA.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  players: any[] = [];
  teams: any[] = [];

  loading$ = this.loader.isLoading$;

  constructor(private nbaApi: NbAService, private loader: LoaderService) { }

  loadPLayers() {
    this.players = [];
    this.nbaApi.getPlayers().subscribe((data) => {
      this.players = data;
    });
  }

  loadTeams() {
    this.nbaApi.getTeams().subscribe((data) => {
      this.teams = data;
    });
  }
}

Добавьте стиль CSS для экрана загрузки — это div со стилями CSS, чтобы цвета полной ширины выглядели как загрузка.

.loading {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
  font-weight: bold;
  color: yellow;
  text-align: center;
  z-index: 1;
  background-color: rgba(43, 39, 39, 0.616);
}

В шаблоне app.component.html итерируйте объект игрока и команд, используя директиву ngFor. Когда пользователь нажимает Load Player или Load Teams, в верхней части экрана появляется сообщение о загрузке.

<div class="items">
  <ul>
    <li *ngFor="let p of players">{{ p.first_name }} {{ p.last_name }}</li>
  </ul>
    <ul>
      <li *ngFor="let t of teams">{{ t.city }} {{ t.conference }}</li>
    </ul>
</div>
<div>
  <button (click)="loadPLayers()" >Load Players</button>
  <button (click)="loadTeams()">Load Teams</button>
</div>
<!-- subscription to loading observable >
  <div class="loading" *ngIf="loading$ | async">
    <h1>Please wait...</h1>
  </div>

Когда пользователь нажимает кнопки Get Players или Get Teams, перехватчик обнаруживает HTTP-запрос и устанавливает для параметра visible значение true, а когда сервер возвращается, данные снова переключаются на значение false.

Поскольку наблюдаемая loading$ подписывается на loadingService, она изменяется каждый раз, когда Subject выдает новое значение.

Окончательный результат выглядит как на картинке:

Заключительные мысли

Мы узнали, как создать Interceptor и использовать его для управления всеми HTTP-запросами в одном месте. Идеальное место для обработки вашего HTTP-запроса. Перехватчик доступен во многих сценариях, чтобы предоставить пользователям отличный опыт.

Вы можете найти полный пример кода для этой статьи и поиграть с примером приложения по следующим ссылкам:

  1. Demo
  2. GitHub
#Angular
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу