We found out today that the following doesn’t work if your app uses JSONP because angular expects the handlers to exist in the global window.angular object. YMMV.
I’ll update if we come up with a fix.
In some instances you may need to include multiple versions of
within the same page. This can happen when writing a third-party widget that
you want your customers to embed onto their pages. Part of being a good citizen
is not stomping all over their global variables and overwriting their version
Trying to load two incompatible versions of angular can also cause errors that take down both your widget and your customer’s site.
Imagine that we are writing a widget that depends on the latest
of the time of this writing, 1.2.7). But our widget is going to be embedded
onto a page that already utilizes a much older version (1.0.4). How can we go
about loading our script and our dependencies without interfering with the
existing page’s scripts?
The answer is not terribly difficult if we are using some sort of build script
angular.js and any standard modules as part of that same payload.
The technique works by wrapping all of our code (including the
in a immediately-invoking function
that declares a local
angular variable. Then, through the magic of closures,
any references in our code to
angular will resolve to that local variable rather
than the global
To wrap up all our code and dependencies, we’re going to prepend an
intro.js file that begins the self-invoking function, and then append an
outro.js that closes up and calls that function.
1 2 3 4 5 6 7 8 9 10 11 12
We’ll look at each part in turn.
Before our script loads, the environment of the webpage looks something like this:
Angular.js version 1.0.4 is already loaded and has saved itself into the global
window.angular variable. We don’t want to override that property, so when we
load the newer
angular.js 1.2.7, we need to trick it into saving itself into a
variable we control.
1 2 3
We do this by saving the current
window.angular into a local variable for
later restoration and then creating a new
window.angular that is simply an
empty object. We also stash a reference to the new
window.angular in a local
At this point, our environment looks something like this:
existingWindowDotAngular points at the 1.0.4 version of
angular.js that was
previously loaded onto the page. Both
window.angular and the local
variable point to the same empty object.
Now we can go ahead and load the version of
angular.js that our widget requires.
angular.js library does not overwrite
window.angular if it’s already
defined, it merely creates it’s data and functions as properties on that
angular and whatever modules we require, we’ll be able to load
our code as normal. Any references in our code to
angular, will resolve to
angular variable we made via closure. Note: in order for this to
work, we should never refer to
window.angular directly. Always simply use
angular so that we correctly resolve to the closed over variable rather than
1 2 3 4
At the end of your file, we concatenate an
outro.js that replaces the
field we saved initially saved off.
1 2 3 4 5 6
We also need to manually bootstrap our app. After restoring
1.0.4, it will be that version that encounters the
attribute. But since
MyWidget was declared as part of a different
instance, it will not be able to find and boot it.
You may find that you have to restore the
existingWindowDotAngular inside the
ready event because the
$httpBackend service unfortunately accesses
$window.angular directly. This does open up a small window for potential
conflict, but the only other option is to patch angular directly.
After loading our widget’s payload, the global
window.angular has been
restored to 1.0.4 and all our code refers to 1.2.7 via the closed over
angular variable. The two
angular.js instances are now completely
Its a good idea to use this technique on any widget that is intended to be
injected into arbitrary webpages (even if there is no existing
there). For the safety of our own app, and the protection of the host page’s
scripts, we should always avoid polluting the global namespace with data
specific to our module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31