Описание проблемы
Если вы разрабатывали приложения на Angular, то наверняка сталкивались с ситуацией, когда множество компонентов требуют тесного взаимодействия друг с другом. Например:
-
Один компонент должен отправлять события другому компоненту (или нескольким), что часто приводит к написанию громоздкого и запутанного кода.
-
Использование Input/Output связей может подходить для простых случаев, но становится затруднительным в масштабируемых приложениях.
-
Проблемы с утечками при работе с RxJS Subjects или Event Emitters — нужно следить за отключением компонентов/сервисов от подписок в конце жизненного цикла.
-
Код становится сильно связанным (tight coupling), что затрудняет поддержку, тестирование и рефакторинг.
Рассмотрим пример: в приложении имеется два компонента (отправитель и получатель), которые должны взаимодействовать. Часто это выглядит примерно так:
// Sender.component.ts import { EventEmitter, Output } from '@angular/core'; @Component({ selector: 'app-sender', template: `<button (click)="sendEvent()">Отправить</button>`, }) export class SenderComponent { @Output() eventFromSender = new EventEmitter<string>(); sendEvent() { this.eventFromSender.emit('Событие от отправителя!'); } } // Receiver.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'app-receiver', template: `<p>{{ message }}</p>`, }) export class ReceiverComponent { @Input() message = ''; }
Соединение этих компонентов происходит на уровне родителя:
<!-- Parent.component.html --> <app-sender (eventFromSender)="handleEvent($event)"></app-sender> <app-receiver [message]="message"></app-receiver>
// Parent.component.ts export class ParentComponent { message = ''; handleEvent(event: string) { this.message = event; } }
На первый взгляд это просто, но если количество таких взаимодействий между компонентами растёт, либо они начинают находиться в разных модулях, ваш код быстро превращается в клубок событий и зависимостей. Сплошные @Input, @Output, Services и Subjects — это как минимум неудобно.
Как помогает @artstesh/postboy
Библиотека @artstesh/postboy создана, чтобы:
-
Снизить связанность кода — больше никаких прямых связей компонентов через Input/Output или событийные Subjects.
-
Упростить архитектуру — теперь каждый компонент может «общаться» с другими через глобальный механизм событий, не зная детали их реализации.
-
Минимизировать зависимости — лёгкий инструмент, который не требует громоздких библиотек или решений.
@artstesh/postboy реализует гибкий и понятный интерфейс для обмена событиями. Вам достаточно «подписаться» на нужное событие и «отправить» его, когда это потребуется. Компоненты не знают друг о друге, но взаимодействуют абсолютно прозрачно.
Теперь посмотрим, как можно упростить предыдущий пример с помощью @artstesh/postboy.
1. Установка библиотеки
Во-первых, добавьте библиотеку в свой проект:
npm install @artstesh/postboy@2
Первоначальная установка потребует от нас создания нескольких дополнительных классов, отвечающих за управление событиями по всему приложению.
Центральный сервис, наследующий PostboyService, расположим его где-нибудь ближе к корню проекта, в папке services:
// src/app/services/app-postboy.service.ts import {PostboyService} from '@artstesh/postboy'; @Injectable({providedIn: 'root'}) export class AppPostboyService extends PostboyService{}
Сервис регистрации событий, отвечающий за управление жизненным циклом подписок:
// src/app/services/app-message-registrator.service.ts import { PostboyAbstractRegistrator } from '@artstesh/postboy'; @Injectable() export class MessageRegistrator extends PostboyAbstractRegistrator { constructor(postboy: AppPostboyService) { super(postboy); } protected _up(): void { } }
Инициализируем регистрацию в AppComponent :
// src/app.component.ts @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], standalone: true, providers: [MessageRegistrator] }) export class AppComponent implements OnDestroy { constructor(private registrator: MessageRegistrator) { registrator.up(); } ngOnDestroy(): void { this.registrator.down(); } }
Подготовка завершена и приложение готово к работе с событиями в рамках подходов @artstesh/postboy
2. Использование библиотеки
Создадим событие(message) ButtonClickEvent
// src/app/messages/button-click.event.ts export class ButtonClickEvent extends PostboyGenericMessage { public static readonly ID = '5861b2ea-74eb-4744-9b04-69468a278c34'; constructor(public text: string){} }
Зарегистрируем его в методе _up() регистратора:
// src/app/services/app-message-registrator.service.ts import { PostboyAbstractRegistrator } from '@artstesh/postboy'; @Injectable() export class MessageRegistrator extends PostboyAbstractRegistrator { // ... protected _up(): void { this.recordSubject(ButtonClickEvent); } }
Вернемся к изначальному примеру и изменим поведение Отправителя и Получателя:
// Sender.component.ts import { EventEmitter, Output } from '@angular/core'; @Component({ selector: 'app-sender', template: `<button (click)="sendEvent()">Отправить</button>`, }) export class SenderComponent { constructor(private postboy: AppPostboyService) {} sendEvent() { this.postboy.fire(new ButtonClickEvent('Событие от отправителя!')); } } // Receiver.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'app-receiver', template: `<p>{{ message }}</p>`,, changeDetection: ChangeDetectionStrategy.OnPush }) export class ReceiverComponent implements OnInit { message = ''; constructor(private postboy: AppPostboyService, private detector: ChangeDetectorRef) {} ngOnInit(): void { this.postboy.sub(ButtonClickEvent).subscribe(ev => { this.message = ev.text; this.detector.detectChanges(); }); } }
Теперь оба компонента взаимодействуют через AppPostboyService, не зная о существовании друг друга. Это упрощает работу, особенно если количество таких взаимодействий растёт.
Что изменилось?
-
Мы полностью избавились от Input/Output связей. Нет необходимости настраивать события через родителя.
-
Реализация стала гибкой: теперь можно легко подключить новые компоненты-слушатели, просто подписав их на событие
ButtonClickEvent. -
Такая архитектура легче тестируется: каждый компонент можно изолировать и протестировать логику отдельно.
Заключение
Если вы хотите упростить архитектуру своего Angular-приложения и сделать взаимодействие компонентов максимально прозрачным, полагаю, что @artstesh/postboy станет весьма интересным вариантом. Она минималистична, проста в освоении и прекрасно интегрируется в существующие проекты.
Ваши вопросы, замечания и предложения очень важны! Делитесь своими идеями и обратной связью в комментариях или пишите мне в личных сообщениях. Больше информации можно найти на сайте проекта.
Спасибо за внимание! 😊
ссылка на оригинал статьи https://habr.com/ru/articles/881716/
Добавить комментарий