In today's article, we will introduce the @artstesh/postboy library — a tool designed to simplify event management, improve application architecture, and make your code more modular and readable. If you're tired of overwhelming services, countless subscriptions, and tricky dependencies, @artstesh/postboy might become your go-to solution for these challenges.
Why Use @artstesh/postboy?
Modern Angular applications often face challenges such as:
- Complexity in testing: tightly coupled services and components;
- High coupling: components often depend heavily on each other;
- Difficult debugging: tracking lost or broken events can be tough;
- Adding new functionality: extending the application can lead to tangled logic.
The @artstesh/postboy library introduces an event-driven approach, making asynchronous and synchronous processes easier to manage. It focuses on clean architecture principles, isolating logic from other layers of your app. This means your project becomes more modular, your code cleaner, and your life as a developer easier.
Example of a Problem
To better understand the benefits of @artstesh/postboy, let's look at a common Angular challenge:
Problem: Using @Input for Communication Between Components
Imagine you have a parent component (ParentComponent
) and two child components (ChildA
and ChildB
). The task is to send a variable’s state from ChildA
to ChildB
. The simplest (and the worst one) Angular way to handle this requires:
- Binding the variable in
ChildA
to its parent component (ParentComponent
) using the @Output decorator and an event emitter. - Passing the updated state from the parent to
ChildB
using @Input.
This approach results in tightly coupled components and a more complex flow. The communication looks like this:
ChildA --> Parent --> ChildB
Downsides of This Approach
- High Coupling: Components are tightly bound through explicit @Input and @Output bindings.
- Difficult to Scale: Adding more components requires manual updates to bindings at multiple levels.
- Hard to Test: Testing the flow of data between the components requires mocking input and output connections.
- Unnecessary Complexity: It introduces extra boilerplate code to achieve something simple, creating dependencies on the parent component.
The Solution with @artstesh/postboy
The @artstesh/postboy library eliminates the dependency chain. Instead of having ChildA
and ChildB
communicate via the parent, you can send an event from ChildA
that ChildB
listens for directly.
Example: Solving the Problem
Step 1. Create an Event
First, define an event that will carry the state from ChildA
to ChildB
:
import { PostboyGenericMessage } from '@artstesh/postboy';
export class UpdateVariableEvent extends PostboyGenericMessage {
public static readonly ID = '5861b2ea-74eb-4744-9b04-69468a278c34';
constructor(public text: string){}
}
Step 2. Emit the Event in ChildA
In ChildA
, emit the event when the variable changes:
import {Component} from '@angular/core';
import { AppPostboyService } from './services/app-postboy.service';
import {UpdateVariableEvent} from './events/update-variable-event';
@Component({
selector: 'app-child-a',
template: `<input [(ngModel)]="variable" (input)="onInputChange()" />`
})
export class ChildAComponent {
variable: string = '';
constructor(private postboy: AppPostboyService) {
}
onInputChange(): void {
postboy.fire(new UpdateVariableEvent(this.variable));
}
}
Step 3. Subscribe to the Event in ChildB
In ChildB
, listen for the event and update the component’s state when it occurs:
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit} from '@angular/core';
import {AppPostboyService} from './services/app-postboy.service';
import {UpdateVariableEvent} from './events/update-variable-event';
@Component({
selector: 'app-child-b',
template: `<p>Updated Value: {{ variable }}</p>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildBComponent implements OnInit {
variable: string = '';
constructor(private postboy: AppPostboyService, private detector: ChangeDetectorRef) {
}
ngOnInit(): void {
postboy.sub(UpdateVariableEvent).subscribe(ev => {
this.variable = ev.text;
this.detector.detectChanges();
});
}
}
What Happens Behind the Scenes?
- When the user updates the input in
ChildA
, theUpdateVariableEvent
is published. -
ChildB
listens for the event and updates itsvariable
property immediately. - There’s no interaction with the parent component, keeping the flow simpler and decoupled.
Advantages of This Event-Driven Approach
Decoupling
ChildA
andChildB
no longer depend directly on the parent component. Updates flow through defined events instead.Scalability
Adding new listeners or publishers for the same event is seamless. For instance, you can add aChildC
component to listen for theUpdateVariableEvent
without modifying the parent orChildA
.Simpler Code
Instead of juggling @Input and @Output decorators with event emitters, you write clear, focused logic for publishing and subscribing to events.Improved Readability
The code is straightforward: one part of the app publishes events, and others react to them. No need to trace convoluted binding hierarchies.Ease of Testing
Events can be mocked and tested independently without needing to recreate the parent-child communication structure.
How to Get Started?
Visit the library's site to read more about the library. There is a simple example of installation, and using of the library in an Angular application here.
Who Can Benefit From the Library?
For Small Applications
Simplify the architecture by reducing the number of services and focusing on modular code.
For Medium and Large Applications
Manage dependencies and messaging in complex systems by dividing the app into independent zones and ensuring loose coupling between modules.
Summary
The @artstesh/postboy library offers a fresh take on event management in Angular, focusing on simplification, readability, and scalability. By switching to an event-driven approach, you get:
- Cleaner, more modular code;
- Testability with isolated logic;
- Freedom from complex subscription and callback chains.
In upcoming articles, we’ll dive deeper into other powerful features of the library, such as working with asynchronous commands, locking mechanisms, and improving testing.
This library has proven its value in multiple projects within a small team of developers, and I decided to share this solution with the community. I would be delighted to receive feedback, including suggestions for extending the functionality and improving the quality of the library
Top comments (0)