Angular developers often find themselves needing to use a third party library in a hurry, and either the third party library is not itself Angular-ready, you can’t find an Angular-wrapped version of it on github/bower/whatever, or the Angular-wrapped version you find is unsuitable for your purposes, out of date, or buggy.
In these cases, it’s pretty tempting to just add the library to your page scripts, and either tell jshint the library is a global and just use it everywhere in your code (gross), or inject $window
into anything you want to use that third party library (gross, but slightly less so).
For example, using momentjs:
Really though, it would be better if momentjs were a module that provided a service you could inject. Makes for more angular-idiomatic code, more sensible testing, and just feels better.
So I’ve started using a relatively simple pattern for accomplishing just that. Disclaimer: there may well be better ways to do this, I’ve just found this method to be nice and quick, and play well with karma for testing.
The basic principle is that for each third party library you want to use, you’re going to make a new module named after that library, and it’s going to provide a service whose name is whatever you were previously using as the global to access it. So for moment.js, our module will be called momentjs
, and our service will be called moment
. That means that we will (in a normal use case), make our app dependent on momentjs
, and inject moment
into anything that wants to use momentjs functionality.
However, I also want moment
to no longer be attached to window
, so I (and any other devs on the project) can’t just use window.moment
globally. So we’re going to delete window.moment
(there are some caveats to this as well, which I’ll cover after this code example). So, here’s how we do it
So now - window.moment
isn’t polluting window
and can’t just be used as a global, but instead has to be injected into anything that wants to use it.
You’ll have noticed in the code example that it wasn’t just a simple case of deleting window.moment
however. If your app doesn’t have unit tests at all (which would be bad, don’t do it!), then instead of the moment
factory method above, you could probably safely rewrite it as follows:
However, if your app does have karma unit tests, you’ll find that your modules are actually getting created once per spec. After the first spec runs, you’ll find that moment
no longer works, and your tests all break in horrifying ways.
This is because in the first run, you delete window.moment
. That’s not a problem for your first spec, because before you deleted window.moment
, your service grabbed a reference to it, so your app can access moment
through the service.
In the second and subsequent specs however, window.moment
has already been deleted, so when the test runner tries to create the momentjs
module for the second and subsequent tests, it fails, because window.moment
is gone and your momentjs
module (being created in second and subsequent specs) can’t grab a reference to moment
to return when creating the service.
As such, the example I give moves moment out of the window/global namespace onto window._thirdParty
. This means that you can’t just grab at moment globally without making a conscious decision to misbehave by getting it via window._thirdParty
, and it means that instead of heaving one window property for every third party library you add, they’re all hidden in a single _thirdParty
property. Karma will be able to succesfully create the momentjs module as many times as it likes as well, as your module creation code gets the moment reference from _thirdParty
(or moves moment to _thirdParty
if it hasn’t already, THEN retrieves the moment reference from _thirdParty
);