DEV Community

Cover image for Asynchronous Events: Getting Rid of Internal Services in Angular applications
Art Stesh
Art Stesh

Posted on

Asynchronous Events: Getting Rid of Internal Services in Angular applications

Asynchronous event management in Angular applications is a core task, particularly when the application works with dynamic data or has a complex architecture. To tackle this issue, developers often rely on subscriptions and/or intermediate services. Despite being widely used, these approaches can lead to excessive dependencies, tight coupling, and more complex code.

The @artstesh/postboy library offers an alternative approach: it allows handling asynchronous events in a simple and concise way, eliminating the need for intermediate services.


How to Fire and Handle Asynchronous Events

Firing Events

To publish events with @artstesh/postboy, you can use a convenient API to fire events with or without parameters. Here's an example:

// Firing an event without parameters
postboy.fire(new SimpleEvent());

// Firing an event with parameters
postboy.fire(new EventWithParams('Hello, world!'));
Enter fullscreen mode Exit fullscreen mode

In this example, the library takes complete care of message delivery. This means you don't have to manually link components through services or rely on Input/Output bindings. It's also worth noting that the library handles completing subscriptions automatically at the end of a module's lifecycle.


Handling Events

To handle events, you can "subscribe" through the library itself. This is not the standard RXJS/Observable subscription but a binding method that removes the need for explicit unsubscriptions.

Here's how you can handle events:

// Subscribing to an event without parameters
postboy.sub(SimpleEvent).subscribe(() => {
  console.log('SimpleEvent was triggered!');
});

// Subscribing to an event with parameters
postboy.sub(EventWithParams).subscribe(message => {
  console.log('Message received:', message);
});
Enter fullscreen mode Exit fullscreen mode

Now, when calling postboy.fire(new SimpleEvent()), the event handler is executed automatically. If the event passes data, it will be available in the handler.


Why This Is Better Than Traditional Approaches

The advantages of using the asynchronous approach with @artstesh/postboy include:

  1. Reduced Code: You no longer need to create intermediate services. A single instance of Postboy can handle communication between all application components (or specific modules, if you decide to separate event zones by using different event registrars).

  2. Improved Code Readability: No more cumbersome chains of subscriptions and state management. The role of each component becomes clearly defined.

  3. Reduced Risk of Memory Leaks: There's no need to explicitly unsubscribe from events as with RXJS. The system automatically completes all subscriptions when a module is destroyed.

  4. Flexible Management: Easily modify application behavior by adding new event handlers, removing or redirecting events without altering the business logic of components or tightly coupling them. This simplifies long-term development and maintenance.


Comparison of Approaches

Let's look at a synthetic scenario: we have a button that triggers a request and displays the result in a component.

Without @artstesh/postboy

In a traditional Angular application, this could look like this:

// middle.service.ts

import { Injectable } from "@angular/core";
import { ReplaySubject } from "rxjs";

@Injectable({ providedIn: 'root' })
export class MiddleService {
  message$ = new ReplaySubject<string>();
}
Enter fullscreen mode Exit fullscreen mode
// button.component.ts

import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-button',
  template: '<button (click)="loadData()">Load Data</button>',
})
export class ButtonComponent {
  constructor(private dataService: DataService, private middle: MiddleService) {}

  loadData() {
    this.dataService.loadData().subscribe((data) => {
      this.middle.message$.next(data.result);
    });
  }
}
Enter fullscreen mode Exit fullscreen mode
// display.component.ts

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

@Component({
  selector: 'app-display',
  template: '{{ data }}',
})
export class DisplayComponent implements OnInit {
  data: string = '';

  constructor(private middle: MiddleService) {}

  ngOnInit() {
    this.middle.message$.subscribe(d => this.data = d);
  }
}
Enter fullscreen mode Exit fullscreen mode

With @artstesh/postboy

Now let's rewrite the same example using the @artstesh/postboy library:

// button.component.ts

import { Component } from '@angular/core';
import { AppPostboyService } from './app-postboy.service';

@Component({
  selector: 'app-button',
  template: '<button (click)="loadData()">Load Data</button>',
})
export class ButtonComponent {
  constructor(private postboy: AppPostboyService) {}

  loadData() {
    this.postboy.fireCallback(new LoadDataCommand(), (response) => {
      // Handle data after loading
      const data = { result: 'Data loaded successfully' };
      this.postboy.fire(new DataLoadedEvent(data));
    });
  }
}
Enter fullscreen mode Exit fullscreen mode
// display.component.ts

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

@Component({
  selector: 'app-display',
  template: '{{ data }}',
})
export class DisplayComponent implements OnInit {
  data: string = '';

  constructor(private postboy: AppPostboyService) {}

  ngOnInit() {
    this.postboy.sub(DataLoadedEvent).subscribe(data => {
      this.data = data.result;
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

For a pair of components, the difference may not seem significant, but as the application grows, the traditional approach becomes loaded with more subscriptions and new services. Meanwhile, the postboy-managed code continues to rely only on the AppPostboyService.


Conclusion

Asynchronous event management using @artstesh/postboy makes the code in Angular applications more readable and easier to maintain. By eliminating the need for intermediate services and Input/Output bindings, event handling becomes significantly simpler.

Don't forget to explore other features of the library, and feel free to share suggestions for improving and extending its functionality!

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay