Основы 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-запроса. Перехватчик доступен во многих сценариях, чтобы предоставить пользователям отличный опыт.
Вы можете найти полный пример кода для этой статьи и поиграть с примером приложения по следующим ссылкам: