Aurelia’s Event Aggregator

Aurelia’s event aggregator is a lightweight pub/sub messaging system which I am going to make use of to update my contact list with edits to a contact are saved.

I started by creating the class that would be used to publish messages to the event aggregator. In this case the message is for notification that a contact was updated and will contain the contact that was updated.

export class ContactUpdatedMessage {
    constructor(contact) {
        this.contact = contact;
    }
}

Detail.js is the view model for editing of contacts needs imports for the new ContactUpdatedMessage and Aurelia’s event aggregator. The ContactUpdatedMessage is in the Aurelia folder of the application’s wwwroot which is why it has “Aurelia/” and not just ContactsUpdatedMessage in the from portion of the import statement.

import {EventAggregator} from 'aurelia-event-aggregator';
import {ContactUpdatedMessage} from 'Aurelia/ContactUpdatedMessage';

Add EventAggregator to the inject decorator of the details class. Next the constructor needs to be changed for injection of the event aggregator. Ignore the fact that the web API url is hardcoded. I know there is a better way to handle the url, but I have not had time to look into it yet. In the next to last line a reference to the injected event aggregator is saved.

@inject(HttpClient, EventAggregator)
export class Detail{

    constructor(http, eventAggregator){
        http.configure(config => {
            config
              .useStandardConfiguration()
              .withBaseUrl('https://localhost:18907/api/');
        });

        this.http = http;
        this.eventAggregator = eventAggregator;
    }

When a user clicks save on a contact the submit function is called. The call to the web API using fetch is an existing part of the submit function. The new bit is the call to the eventAggregator’s publish function with new ContactUpdatedMessage using the edited contact.

submit() {
    this.http.fetch('contacts/' + this.contact.Id, 
                    { method: 'put', 
                      body: json({id: this.contact.Id, 
                                  name: this.contact.Name}) })
             .then(response => response.json())
             .then(contact => this.contact = contact)
             .catch(e => {
                    console.log(e);
                    this.postResult = e.status + '-' + e.statusText;
                    });

    this.eventAggregator.publish(new ContactUpdatedMessage(this.contact));
}

List.js needs to be a subscriber for the ContactsUpdateMessage. The class needs the same new imports and injection of the event aggregator as the detail class. In the list class instead of saving a reference to the event aggregator its subscribe function will be used to listen for the contact updated messages. The second part of the subscribe function takes a function to be called when a contact updated message is received. In this example when a contact updated message is received the function finds the proper contact and replaces it with the updated version.

import {EventAggregator} from 'aurelia-event-aggregator';
import {ContactUpdatedMessage} from 'Aurelia/ContactUpdatedMessage';

@inject(HttpClient, EventAggregator)
export class List{
    contacts = [];

    constructor(http, eventAggregator){
        http.configure(config => {
            config
              .useStandardConfiguration()
              .withBaseUrl('https://localhost:18907/api/');
        });

        this.http = http;
        eventAggregator.subscribe(ContactUpdatedMessage, message => {
            let updated = this.contacts
                              .filter(contact => contact.Id == message.contact.Id)[0];
            Object.assign(updated, message.contact);
        });
    }

}

With the above changes any edits to a contact will be reflected in the contact list as soon as the contact is saved.

6 thoughts on “Aurelia’s Event Aggregator”

  1. This is a fantastic post! It would be great if it could be made a bit more generic and sent to the Aurelia team as an article on the official docs page. Given the sparsity of the API docs, did you just figure out how to use the aggregator by looking at the source?

  2. In this example what are the advantages of using Event Aggregator over standard two-way binding. Is there a performance, design improvement?

    1. This example was just an experiment in the usage of the event aggregator. In an actual application I could see it being useful when multiple parts of the application need notification of an event. It also provides a means to decouple parts of your application allowing them to use events instead of having to have a direct reference to the other components.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.