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