Angular and legacy JavaScript integrations

How to make your Angular SPAs speak "well" with legacy libraries

Jose I Santa Cruz G
ITNEXT

--

Dilbert ( https://dilbert.com/strip/2006-12-08 )

Most systems that provide certain features that are commonly used, also provide some kind of integration. Let's say that graphics and charts library, documented in old-plain legacy JavaScript (< ES5), with old styled HTML &<script> code, just to include a bar chart. Or some site that allows visitors buy event tickets, no API, no web components, just a block of code with a "insert this script on your code" which imports a Javascript file, a CSS file and embeds an iframe. You probably have seen this, and if you haven't you'll do sometime (trust me…)

This integrations, most of the times, are meant for non-developers, "include this code and it will work", magic.

But, what if you have to make this integrations work with your Angular SPA?

I recently fought against iFrame resizer library , had to add some 3rd party code block that included an iframe, a js file, and a CSS file to an Angular component. "To improve UX add this script to the head of your HTML…" and there comes iframe resizer. By the way iframes in absolutely NO WAY improve UX, iframes just look as a Post It on a screen.
Short story, I read the docs, and none of the recommendations worked for me.

So here's what I did:

Dynamically load all external required files

This will be adding the required scripts and css to the DOM but not by pasting the code on the component's template. We'll be using a service for this:

https://gist.github.com/jsanta/9c845b6be5bc2118d0a1415f2c3c569f

This service takes an URL for the script or CSS file and appends it to the HTML DOM. Include it as a provider in your app.module.ts file so you can have a single instance of the service and avoid loading the files if you already loaded them.

Declare a global variable to wrap the script

On your component you'll need to declare a wrapper for the 3rd party script you're trying to include. There's a slight hope you know the internals of this script, so you can define an interface that may allow autocompleting the code. As we usually don't, we'll just use the any type.

https://gist.github.com/jsanta/231bb5c65a1faf7571c846dfc49a3263

Why global? Well, in the company I'm working for, we have some dynamic form validations that are loaded this way. We were having some issues with Safari and declaring the script object variables as global solved the problem.

and finally…

Use the 3rd party script as suggested

You can now use the scripts as shown on the documentation (the code must be included after being sure the scripts were loaded, this is between lines 34 and 36. For eg.:

// ...    Promise.all(scriptArray).then(_ => {
console.log('All scripts loaded');
// These are not real methods, just placed them
// for illustrative purposes
externalLibrary.loadData(this.data);
externalLibrary.renderChart('barchart', 'chart-container');
otherExternalLibrary.validateRoles();
)};// ...

By the way, don't just copy paste the code on the GISTs, I think I'm missing a few closing brackets :)

What about jQuery?

Yes, there's still people that use jQuery. jQuery is another story, once tried loading it this way and failed. So ended finding out that the only way jQuery did work was to add it on the last lines of the polyfills.ts file like this:

import * as jQuery from 'jquery';
window['$'] = jQuery;

and you should be able to use $ as usual.

Hope it helps.

--

--

Writer for

Polyglot senior software engineer, amateur guitar & bass player, geek, husband and dog father. Not precisely in that order.