Creating Ember CLI Addon with External Library. Part 2

In my previous post I covered basics about how to wrap the third-party library in Ember CLI addon, so it can be reusable, and used in Ember-way.

Note: This article will cover only a few issues that you might face during addon development, so I wouldn’t say that it’s a tutorial.

In this part I will try to finish the ember-cli-webfontloader addon by doing following:

trying to find a nice solution of managing weird WebFontLoader’s event system logic

restructuring the addon to isolate it a bit more

Problem

After reading the WebFontLoader docs, I found that they are using probably the strangest event system you can find out there. The problem is that you need to use either a global variable or you need to pass configuration directly to .load() function which we use to load fonts.

In our case, we want the configuration to contain only the fonts we want to load, so we need to find a way of having something more flexible.

My first idea was to create a service that will attach its generic functions to configuration object, which will call our “real” event callbacks on event. But unfortunately this idea failed after a few atempts as I couldn’t find a way of injecting a service to initializer, so I decided to go a bit lame way, I decided to extend WebFont object.

We need to track somehow the state of WebFontLoader, add event handlers and if the event is already active (such as active , loading etc.) we need to be able to run the callback immediately. I will implement two functions: on(string event, function callback, boolean runIfActive) and off(string event, function callback) .

Idea is such that on() function will add callbacks to the list and when an event is fired, the main event handler will execute all callbacks from the appropriate list.

The full list of events is:

loading

active

inactive

fontloading

fontactive

fontinactive

Here I will focus on covering the first three events: loading, active, inactive.

Solution

Let’s outline the extension to the library:

// ember-cli-webfontloader/app/initializers/ember-cli-webfontloader.js export function initialize (...) { ... let events = { // current state of WebFontLoader state : null , // Collection of callback functions to be called // when the appropriate event is fired eventHandlers : { active : [], inactive : [], loading : [], }, // Real `active` callback onActive () { events . state = 'active' ; }, // Real `inactive` callback onInactive () { events . state = 'inactive' ; }, // Real `loading` callback onLoading () { events . state = 'loading' ; } }; ... }

To extend the plugin in a good manner, we need to have some sort of an isolated container. Let’s call ours __events__ :

// ember-cli-webfontloader/app/initializers/ember-cli-webfontloader.js export function initialize (...) { ... // Extending WebFont WebFont . __events__ = events ; ... }

And let’s define our on() function directly on the WebFont object so in can be accessable from outer world:

// ember-cli-webfontloader/app/initializers/ember-cli-webfontloader.js export function initialize (...) { ... // Adds a callback to the eventHandlers[event] list and // if "toRun" is true and the current state is event name, // we need to run callback function after we add it to the list WebFont . on = ( event , callback , toRun ) => { // Adding new handler to the list WebFont . __events__ . eventHandlers [ event ]. push ( callback ); // Checking if we should run it now if ( toRun && event === WebFont . __events__ . state ) { callback (); } }; ... }

Cool, looks promising. Now we need to attach these onActive , onInactive and onLoading functions to the WebFontLoader configuration object.

// ember-cli-webfontloader/app/initializers/ember-cli-webfontloader.js export function initialize (...) { ... // Getting all font families that should be loaded from // configuration let config = Ember . get ( ENV , 'webFontConfig' ) || {}; config . loading = events . onLoading ; config . active = events . onActive ; config . inactive = events . onInactive ; WebFont . load ( config ); ... }

After these steps we can inject the WebFont object to the component/route/whatever and do the following:

// ember-cli-webfontloader/tests/dummy/app/routes/application.js import Ember from 'ember' ; import { WebFont } from 'webfontloader' ; export default Ember . Route . extend ({ actions : { didTransition () { WebFont . on ( 'inactive' , () => { console . log ( 'inactive!' ); }, true ); WebFont . on ( 'loading' , () => { console . log ( 'loading!' ); }, true ); WebFont . on ( 'active' , () => { console . log ( 'active!' ); }, true ); } } });

By doing this we will create event handlers that will be executed on each of three events we covered here: loading, active, inactive.

Good Practice

As some of you could notice, we’re injecting our initializer directly to the application namespace, which is not a good idea, let’s keep our application namespace for an application.

We’ll create a new file addon/initializers/ember-cli-webfontloader.js and copy the contents of the according file from app/ folder and switch from exporting an initializer to export a function that will accept a configuration file, to which we have access only from app namespace:

// addon/initializers/ember-cli-webfontloader.js import { WebFont } from 'webfontloader' ; export default function setupWebFont ( config ) { // Extending WebFont let events = { ... }; // Setting up our functions config . loading = events . onLoading ; config . active = events . onActive ; config . inactive = events . onInactive ; config . fontloading = events . onFontloading ; config . fontactive = events . onFontactive ; config . fontinactive = events . onFontinactive ; // Load with an updated configuration WebFont . load ( config ); }

After doing that we still need to export an initializer to app namespace, but we’ll do this more gently:

// app/initializers/ember-cli-webfontloader.js import Ember from 'ember'; import ENV from '../config/environment'; import setupWebFont from 'ember-cli-webfontloader/initializers/ember-cli-webfontloader'; export default { name: 'ember-cli-webfontloader', initialize() { const config = Ember.get(ENV, 'webFontConfig') || {}; setupWebFont(config); } };

Ok, that’s it, we keep our actual initializer in addon/ folder and here we just pass environment configuration.

Done.

If you have any comments and/or found some lame mistakes here, please let me know in the comments section below.