@aedart/js-events

Event Dispatcher for ES6

Usage no npm install needed!

<script type="module">
  import aedartJsEvents from 'https://cdn.skypack.dev/@aedart/js-events';
</script>

README

Js Events

Event Dispatcher for ES6

Before you continue reading, you should know that this package is heavily inspired by Taylor Otwell's Event Dispatcher in Laravel. Please go read the documentation, to gain a better understanding of what a Service Container is and how they are intended to work... and please do support Laravel!

How to install

npm install @aedart/js-events

Quick Start

Prerequisite

This package assumes that you are using an IoC Service Container and that you have registered the Timer service, via a Service Registrar or similar.

Please make sure that you have some prior knowledge about those packages / components before attempting to use this package.

Service Provider

You need to register the EventServiceProvider before you can obtain the event dispatcher.

By default, the event dispatcher is registered as a singleton instance.

Mixin Event Dispatcher

Once you have registered the event service provider, you can mixin the event Dispatcher into your components, by making use of Mixwith utility.

'use strict';

import { DispatcherAware } from '@aedart/js-events'
import { mix } from '@vestergaard-company/js-mixin';

class MyApplication extends mix(Object).with(
    DispatcherAware
){
    constructor(){
        super();
    }
    
    init(){
        // Dispatcher is available as a property;
        let d = this.dispatcher;
    }
}

export default MyApplication;

Listen for Events

Via Callback Listener

The easiest way to listen for event, is via callback listeners. Once the event is dispatched, the callback is invoked.

'use strict';

import { DispatcherAware } from '@aedart/js-events'
import { mix } from '@vestergaard-company/js-mixin';

class MyApplication extends mix(Object).with(
    DispatcherAware
){
    // ... constructor not shown ...///
    
    init(){
        // Unique Event name or id 
        let event = 'My-Event-Id';
        
        // Callback listener
        let listener = (event, payload) => {
            // Do something when the event has occurred
            console.log(event, payload);
        }
        
        // Listen for the event
        this.dispatcher.listen(event, listener);
    }
}

export default MyApplication;

Via Listener Class

Sometimes it is more convenient to create a dedicated Listener class. This is especially true, if you have complex logic that need to be performed and you wish to encapsulate that logic separate from the rest of you application.

Listener Class

'use strict';

import { Listener } from '@aedart/js-events';

class NotifyUser extends Listener{
    
    handle(event, payload){
        alert('The ' + event + ' event has occurred');
    }
}

export default NotifyUser;

Register Listener

'use strict';

import NotifyUser from './MyListeners/NotifyUser';
import { DispatcherAware } from '@aedart/js-events'
import { mix } from '@vestergaard-company/js-mixin';

class MyApplication extends mix(Object).with(
    DispatcherAware
){
    // ... constructor not shown ...///
    
    init(){
        // Unique Event name or id 
        let event = 'My-Event-Id';

        // Listen for the event
        this.dispatcher.listen(event, new NotifyUser());
    }
}

export default MyApplication;

Via Subscriber

If you application grows, it might make sense to group listeners. This can be achieved via a Subscriber class.

Subscriber class

'use strict';

import NotifyUser from './MyListeners/NotifyUser';
import SaveStatus from './MyListeners/SaveStatus';
import RedirectToLogin from './MyListeners/RedirectToLogin';
import { Subscriber } from '@aedart/js-events';

class MySubscriber extends Subscriber{
    
    constructor(){
        this.listeners = new Map();
        
        // Key = event name / id
        // Value = array of listeners
        this.listeners.set('my-event-id', [
            new SaveStatus(),
            new NotifyUser()
        ]);
        
        this.listeners.set('my-other-event-id', [
            new RedirectToLogin()
        ]);        
    }
    
}

export default MySubscriber;

Subscribe to the events

'use strict';

import MySubscriber from './Subscribers/MySubscriber';
import { DispatcherAware } from '@aedart/js-events'
import { mix } from '@vestergaard-company/js-mixin';

class MyApplication extends mix(Object).with(
    DispatcherAware
){
    // ... constructor not shown ...///
    
    init(){
        // Subscribe
        this.dispatcher.subscribe(new MySubscriber());
    }
}

export default MyApplication;

Dispatch Events

To dispatch an event, you simply invoke the fire method on the dispatcher.

'use strict';

import { DispatcherAware } from '@aedart/js-events'
import { mix } from '@vestergaard-company/js-mixin';

class MyApplication extends mix(Object).with(
    DispatcherAware
){
    // ... not shown ...///
    
    saveFormData(data){
        
        // Create the payload object
        let payload = {
            data: data,
            application: this
        }; 
        
        // Dispatch event
        this.dispatcher.fire('save-form-data', payload);
    }
}

export default MyApplication;

Dispatch Events in the Background

If you are not required to wait until all listeners have been handled, then you can dispatch an event in the background. This means is that a setTimeout is created, with 0 delay, in which all registered listeners are handled.

'use strict';

import { DispatcherAware } from '@aedart/js-events'
import { mix } from '@vestergaard-company/js-mixin';

class MyApplication extends mix(Object).with(
    DispatcherAware
){
    // ... not shown ...///
    
    saveFormData(data){
        
        // Create the payload object
        let payload = {
            data: data,
            application: this
        }; 
        
        // Dispatch event in the background - we do not wait for this to finish
        this.dispatcher.fire('save-form-data', payload, true);
        
        // Do something else... code is invoked immediately
        this.lastFormData = data;
        
        // ... continue with other logic here ... ///
    }
}

export default MyApplication;

Stop Event Propagation

Should you need to stop events from propagating, then your listener(s) just need to return false, in their handle method.

The same is true if you listeners is a callback!

'use strict';

import { Listener } from '@aedart/js-events';

class NotifyUser extends Listener{
    
    handle(event, payload){
        
        // Stop other listeners from reacting to the event
        return false;
    }
}

export default NotifyUser;

Background Listeners

Just like when you wish to dispatch events in the background, you can create listeners that process an event in the background, which is separate from the dispatching logic.

In other words, if you wish to execute "slow / time demanding" logic independently from other listeners, then you can create a BackgroundListener. As soon as it is triggered, it will create a timeout via setTimeout, and execute it's logic after a specifiable delay.

Warning Background listeners are unable to stop event propagation.

'use strict';

import { BackgroundListener } from '@aedart/js-events';

class NotifyUser extends BackgroundListener{
    
    constructor(){
        // Set delay in milliseconds of when this listener
        // should process the event.
        this.processDelay = 500; // Optional, default is 0 (zero)
    }
    
    // Process event in the background
    // WARNING: cannot stop event propagation!
    process(event, payload){
        // ... logic not shown ...//
    }
}

export default NotifyUser;

Onward

For further API reference, please review the source code.

Contribution

Have you found a defect ( bug or design flaw ), or do you wish improvements? In the following sections, you might find some useful information on how you can help this project. In any case, I thank you for taking the time to help me improve this project's deliverables and overall quality.

Bug Report

If you are convinced that you have found a bug, then at the very least you should create a new issue. In that given issue, you should as a minimum describe the following;

  • Where is the defect located
  • A good, short and precise description of the defect (Why is it a defect)
  • How to replicate the defect
  • (A possible solution for how to resolve the defect)

When time permits it, I will review your issue and take action upon it.

Fork, code and send pull-request

A good and well written bug report can help me a lot. Nevertheless, if you can or wish to resolve the defect by yourself, here is how you can do so;

  • Fork this project
  • Create a new local development branch for the given defect-fix
  • Write your code / changes
  • Create executable test-cases (prove that your changes are solid!)
  • Commit and push your changes to your fork-repository
  • Send a pull-request with your changes
  • Drink a Beer - you earned it :)

As soon as I receive the pull-request (and have time for it), I will review your changes and merge them into this project. If not, I will inform you why I choose not to.

Acknowledgement

Versioning

This package follows Semantic Versioning 2.0.0

License

BSD-3-Clause, Read the LICENSE file included in this package