Event Aggregator Use Cases - ghiscoding/aurelia-slickgrid GitHub Wiki

Use Case #1 (most common)

A typical use case is to use the Event Aggregator as a global pub/sub (publish/subscribe). A user would typically send (publish) data (which could be any type of data) with a key identifier which is used as a Singleton Service and another VM or Service would listen (subscribe) to a particular message sent with a key identifier and do something according to the data received.

Example with Parent/Child communication

// Child (sending user form)
import { EventAggregator } from 'aurelia-event-aggregator';

@inject(EventAggregator)
export class HelloChild{

  constructor(private ea: EventAggregator) { }

  save(user) {
    this.ea.publish('form:saved', user);
  }
}
// Parent (listening to user form)
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';

@inject(EventAggregator)
export class HelloParent {
  subscription: Subscription;

  constructor(private ea: EventAggregator) {
    this.subscription = this.ea.subscribe('form:saved', (user) => alert(`Hello ${user.firstName}`));
  }

  dispose() {
    // don't forget to dispose of the subscription to avoid any side effect
    this.subscription.dispose();
  }
}

Use Case #2 - Plugin with Internal Communication

Another use case is if we develop a plugin (say a data grid) and we want to have internal communication within that plugin but not outside of it and we also want to make sure that if we reuse the plugin multiple times in a View, then their communications will be contained. A good example would be that if we create 2 grids in a View and the first grid sends a message saying that it cleared all its filters, well we obviously don't want any other grid to react to this message, just the first one (or the originator).

Example with Plugin Communication

// Plugin
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';

@inject(NewInstance.of(EventAggregator))
export class MyGridPlugin{
  subscription: Subscription;
  searchFilter = 'something';

  constructor(private ea: EventAggregator) {
    this.subscription = this.ea.subscribe('filter:cleared', () => this.searchFilter = '');
  }

  dispose() {
    this.subscription.dispose();
  }
}
// Service
import { EventAggregator} from 'aurelia-event-aggregator';

@inject(EventAggregator)
export class FilterService {
  constructor(private ea: EventAggregator) { }

  clear() {
    this.ea.publish('filter:cleared', true);
  }
}

Use Case #3 - Plugin with Internal Communication & Global Communication

Very similar to the previous use case, except that not only do we want internal communication, we also want to have global communication. An example of a global communication would be that we want to do a certain action in the plugin when the I18N published a locale changed, for example changing the text of a grid pagination. For that we can use the NewInstance.of(EventAggregator).as(AnotherClass), this will allow us to have 2 instance of the Event Aggregator (1 being local to the plugin and the other being a global one).

Example with Plugin Communication

Create a Plugin Event Aggregator (EA used for the plugin communication)
import { Disposable } from 'aurelia-framework';

/**
 * A class that will be used for internal communication of parent-child
 * All methods are abstract for typings purposes only
 */
export abstract class PluginEventAggregator {
  abstract publish(event: string, data: any): void;

  abstract subscribe(event: string, callback: (data: any) => void): Disposable;
}
Use both Event Aggregators (global + plugin)
// Plugin
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';

@inject(
  EventAggregator,                                          // Global EA
  NewInstance.of(EventAggregator).as(PluginEventAggregator) // Plugin EA
)
export class MyGridPlugin{
  subscriptions: Subscription[] = [];
  searchFilter = 'something';

  constructor(private globalEa: EventAggregator, private pluginEa: PluginEventAggregator) {
    this.subscriptions.push(
      this.pluginEa.subscribe('filter:cleared', () => this.searchFilter = ''),
      this.globalEa.subscribe('i18n:locale:changed', (payload) => this.translatePaginationTexts(payload))
    );
  }

  dispose() {
    subscriptions.forEach((subscription: Subscription) => this.subscription.dispose());
  }
}
// Service
import { EventAggregator} from 'aurelia-event-aggregator';

@inject(EventAggregator)
export class PaginationService {
  constructor(private ea: EventAggregator) { }

  clear() {
    this.ea.publish('filter:cleared', true);
  }
}

Use Case #4 (Extending Event Aggregator)

In some cases, we maybe want to extend the Event Aggregator and add some functionalities. For example, say we want to know the entire list of subscribe events.

Example

Extending the Event Aggregator
export class ExtendedEventAggregator extends EventAggregator {
  subscriberNames: string[] = [];

  getAllSubscribedEventNames(): string {
    return this.subscriberNames;
  }
 
  // override the EA parent method with our own implementation and call the parent method when we're done
  // in our demo we'll simply keep track of the event key identifier pushed into an array
  subscribe(event: string, callback: (data: any) => void): Disposable {
    this.subscriberNames.push(event); 
    return super.subscribe(event, callback);
  }
}
Use our extended EA
// Parent (listening to user form)
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';
import { ExtendedEventAggregator } from './extendedEventAggregator';

@inject(ExtendedEventAggregator)
export class HelloParent {
  subscription: Subscription;
  subEventNames: string;

  constructor(private extendedEa: ExtendedEventAggregator) {
    this.subscription = this.extendedEa.subscribe('form:saved', (user) => alert(`Hello ${user.firstName}`));
  }

  getSubscribedEvents() {
    // should return:: "form:saved"
    this.subEventNames = this.ea.getAllSubscribedEventNames().join(', ');
  }

  dispose() {
    this.subscription.dispose();
  }
}