npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@thenja/event-manager

v2.0.1

Published

Easily manager events in web applications, works great with Angular.

Downloads

112

Readme

Test Coverage-shield-badge-1

Event Manager

Event manager is an easy way to manage events in a web applications. Its a basic class that you can use to add extra event functionality using methods like: emit(), on(), off() and so on.

Key features

  • Bind and unbind to events using a decorator
    • Reduce code by automatically binding and unbinding to events with a method decorator
    • Works great with Angular components, binds on the ngOnInit method and unbinds on the ngOnDestroy method
  • Run code after all event listeners have executed
    • Easily run code after all event listeners have completed. Works with async event listeners as well
  • Small & light weight
  • Inheritance or Composition
    • Use the EventManager class via inheritance or composition

How to use

  1. Install the module:

npm install @thenja/event-manager --save

  1. You have two ways to use the EventManager. Inheritance via extends or composition via property composition.

Inheritance:

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

// Important: event names have to be unique for the whole application
const USER_EVENTS = {
  SIGN_IN: 'user-sign-in'
};

export class UserService extends EventManager {
  constructor() {
    super(USER_EVENTS);
  }

  userSignIn() {
    this.emit(USER_EVENTS.SIGN_IN, {});
  }
}

// Now you could listen to events
userService.on(USER_EVENTS.SIGN_IN, () => {});

Composition with property (recommended):

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

// Important: event names have to be unique for the whole application
const USER_EVENTS = {
  SIGN_IN: 'user-sign-in'
};

export class UserService {
  events = new EventManager(USER_EVENTS);

  userSignIn() {
    this.events.emit(USER_EVENTS.SIGN_IN, {});
  }
}

// Now you could listen to events
userService.events.on(USER_EVENTS.SIGN_IN, () => {});

Methods / API

constructor (emittedEvents: { [key: string]: string })

The possible emitted events that the manager can emit.

emit (eventName: string, data?: any)

Emit an event. The second parameter is the data that is passed to the listener function.

.on (eventName: string, fn: (data?: any, next?: INextFn) => void, scope?: any)

Bind to an event. If the emitted event sends data, it will be the first parameter.

.once (eventName: string, fn: (data?: any, next?: INextFn) => void, scope?: any)

Bind to an event once, the listener will only fire once.

.off (eventName: string, fn: (data?: any, next?: INextFn) => void)

Unbind from an event.

.offAll (eventName?: string)

Unbind from all events. If you pass in an eventName, it will only unbind all listeners for that event.

@EventListener decorator

The @EventListener decorator is a method decorator that will automatically subscribe and unsubscribe to events, its very useful in Angular components, but can be used anywhere.

IMPORTANT: If using the EventListener decorator, make event names unique throughout your app. If not, the EventListener decorator will throw an error.

How it works:

The decorator simple binds the event on an initialisation method, in the case of Angular, its the ngOnInit method. It unbinds the event on a destroy method, in the case of Angular, its the ngOnDestroy method.

The decorator can be used in two ways:

Two individual arguments:

@EventListener(eventName: string, classObject?: Object)
  • eventName : The name of the emitted event.
  • classObject: (optional) The class that is emitting the event. (view example 1 below in the Use cases / Examples section). If the listener method is listening to internal events that are emitted from within the same class, this can be left blank (view example 2 below in the Use cases / Examples section).

One object argument:

@EventListener(args: IEventListenerArgs)

// Example
@EventListener({
  eventName: 'user-sign-in',
  eventClass: UserService.name,
  initFn: 'ngOnInit',
  destroyFn: 'ngOnDestroy'
})
  • eventName : Same as above
  • eventClass : The constructor name of the class that is emitting the event.
  • initFn : [Default = ngOnInit] The function that is fired when the component / class is initialised. This is where binding to events will occur. If you want to bind to events when the constructor is fired, view example 3 below in the Use cases / Examples section.
  • destroyFn : [Default = ngOnDestroy] The function that is fired when the component is destroyed. This is where unbinding from events will occur.

Use cases / Examples

Example 1 - Listen to an event thats emitted from a service inside an Angular component:

In this example, we have a service that is injected into an Angular component, the service emits events, the component can bind to these events.

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

const USER_EVENTS = {
  SIGN_IN: 'user-sign-in'
};

// our service which extends EventManager
export class UserService extends EventManager {
  constructor() {
    super(USER_EVENTS);
  }

  userSignIn() {
    const userData = {};
    this.emit(USER_EVENTS.SIGN_IN, userData);
  }
}

// our angular component
@Component(...)
export class HomePageComponent {
  constructor(private userSrv: UserService) {}

  @EventListener(USER_EVENTS.SIGN_IN, UserService)
  userSignInListener(userData: any) {
    // This method will be fired when the user-sign-in event is emitted
  }
}

OR you could use composition like:

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

const USER_EVENTS = {
  SIGN_IN: 'user-sign-in'
};

// our service which extends EventManager
export class UserService {
  events: EventManager;

  constructor() {
    this.events = new EventManager(USER_EVENTS);
  }

  userSignIn() {
    const userData = {};
    this.events.emit(USER_EVENTS.SIGN_IN, userData);
  }
}

// our angular component
@Component(...)
export class HomePageComponent {
  constructor(private userSrv: UserService) {}

  @EventListener(USER_EVENTS.SIGN_IN, UserService)
  userSignInListener(userData: any) {
    // This method will be fired when the user-sign-in event is emitted
  }
}

Example 2 - Listen to internal events:

In this example, we will listen to internal events that are emitted within the same class.

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

const USER_EVENTS = {
  SIGN_IN: 'user-sign-in'
};

export class UserService extends EventManager {
  constructor() {
    super(USER_EVENTS);
  }

  userSignIn() {
    const userData = {};
    this.emit(USER_EVENTS.SIGN_IN, userData);
  }

  @EventListener(USER_EVENTS.SIGN_IN)
  userSignInListener(userData: any) {
    // This method will be fired when the user-sign-in event is emitted
  }
}

Example 3 - Bind to events inside constructor:

In this example, we set different init and destroy functions. In this case, we bind to events when the constructor is fired.

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

const USER_EVENTS = {
  SIGN_IN: 'user-sign-in'
};

export class UserService extends EventManager {
  constructor() {
    super(USER_EVENTS);
    this.init();
  }

  protected init();
  destroy();

  @EventListener({
    eventName: USER_EVENTS.SIGN_IN,
    initFn: 'init',
    destroyFn: 'destroy'
  })
  userSignInListener(userData: any) {
    // This method will now be bound to the event when the constructor fires
  }
}

Example 4 - Run code after all event listeners have completed execution:

In this example, we will run code after all event listeners have finished executing. In this example, we are not using the EventListener decorator, but you could still do the same with it.

import { EventManager, EventListener, INextFn } from '@thenja/event-manager';

const USER_EVENTS = {
  SIGN_OUT: 'user-sign-out'
};

// our user settings service
class UserSettingsService {
  constructor(private appEventsHub: AppEventsHub) {
    this.appEventsHub.on(USER_EVENTS.SIGN_OUT, this.userSignedOutListener, this);
  }

  private userSignedOutListener(data, next: INextFn) {
    setTimeout(() => {
      // fire the next function to indicate we are done
      next();
    }, 10);
  }
}

// our user service
class UserService {
  constructor(private appEventsHub: AppEventsHub) {
    this.appEventsHub.on(USER_EVENTS.SIGN_OUT, this.userSignedOutListener, this);
  }

  private userSignedOutListener(data, next: INextFn) {
    setTimeout(() => {
      // the next function returns a completed function, you can set a callback
      // function that gets fired when all listeners have completed and fired
      // their next() functions.
      next().completed(() => {
        // all listeners are done...
      });
    }, 5);
  }
}

// our app events hub service
class AppEventsHub extends EventManager {
  constructor() {
    super(USER_EVENTS);
  }

  userSignedOut() {
    this.emit(USER_EVENTS.SIGN_OUT);
  }
}

Example 5 - Setting the scope

Most of the time you will want to set the scope to this so that the keyword this inside your listener function points to your class instance.

const USER_EVENTS = {
  SIGN_OUT: 'user-sign-out'
};

class UserService {
  private userIsSignedIn = true;
  constructor(private appEventsHub: AppEventsHub) {
    // set this as the scope
    this.appEventsHub.on(USER_EVENTS.SIGN_OUT, this.userSignedOutListener, this);
  }

  private userSignedOutListener(data, next: INextFn) {
    this.userIsSignedIn = false;
  }
}

Development

npm run init - Setup the app for development (run once after cloning)

npm run dev - Run this command when you want to work on this app. It will compile typescript, run tests and watch for file changes.

Distribution

npm run build -- -v <version> - Create a distribution build of the app.

-v (version) - [Optional] Either "patch", "minor" or "major". Increase the version number in the package.json file.

The build command creates a /compiled directory which has all the javascript compiled code and typescript definitions. As well, a /dist directory is created that contains a minified javascript file.

Testing

Tests are automatically ran when you do a build.

npm run test - Run the tests. The tests will be ran in a nodejs environment. You can run the tests in a browser environment by opening the file /spec/in-browser/SpecRunner.html.

License

MIT © Nathan Anderson