Angular2 with Typescript, how to use Plupload CDN script file in lazy-loaded module? Angular2 with Typescript, how to use Plupload CDN script file in lazy-loaded module? angular angular

Angular2 with Typescript, how to use Plupload CDN script file in lazy-loaded module?


Here's the overview of what you need to do to create a lazy-loaded Plupload feature while loading Plupload from a CDN:

  1. When the feature is needed (e.g. user clicks a button or visits a page), dynamically add a <script> tag to the page to load the Plupload library from a CDN.
  2. Wait until the library is loaded to proceed (or you could get a "plupload is undefined" error).
  3. Display the UI to interact with Plupload in one of your Angular templates. In its simplest form, this UI consists of two buttons: "Select files" and "Upload files".
  4. Initialize Plupload and wire it up to the UI.

Complete, working code: https://plnkr.co/edit/4t39Rod4YNAOrHmZdxqc?p=preview

Please take note of the following points in my implementation:

  • Regarding #2. A better way to check whether Plupload has finished loading would be to poll the global namespace for the existence of the plupload variable. As long as window.plupload does not exist, it means the library hasn't been loaded yet and that we should NOT proceed. For simplicity my code just waits for one second and proceeds.
  • Number 4 can prove a bit tricky. Plupload makes a heavy use of direct DOM access to wire its API to the HTML (e.g. document.getElementById('filelist')). This is something Angular discourages and that you should try avoiding whenever possible. More specifically direct DOM access is used in the following places:
    • To tell Plupload which DOM element should trigger the "Select files" dialog (what they call the browse_button config option). For this I could not avoid the direct DOM reference and I used the @ViewChild decorator to get a hold of the "Select Files" button.
    • To display selected files in the template. For this I converted the Plupload syntax into the regular Angular syntax. I push selected files to a class property called fileList which I display in the template using a standard *ngFor.
    • The "Upload Files" button triggers some code that does the actual uploading and refreshes the UI to show upload progress. Once more, I converted this to regular Angular syntax using event binding and data binding.

Let me know if you have any questions.


In this approach no need for any extra loader modules.

See example (check console for Woohoo): http://plnkr.co/edit/gfUs4Uhe8kMGzPxwpBay?p=previewupdated plunker: https://plnkr.co/edit/leG062tg7uX8sLrA0i2i?p=preview

You can lazyload some js by adding the script url to you document:

Create a my-lazy-load.function.ts:

export function lazyload(url) {    // based on https://friendlybit.com/js/lazy-loading-asyncronous-javascript/    let scripts = document.getElementsByTagName('script');    for (let i = scripts.length; i--;) {        if (scripts[i].src.match(url)) return true;    }    let s = document.createElement('script');    s.type = 'text/javascript';    s.async = true;    s.src = url;    let x = document.getElementsByTagName('script')[0];    x.parentNode.insertBefore(s, x);    return true;}

In your component that you want to add plupload:

import {lazyload} from "./my-lazy-load.function.ts";export class MyComponent  implements OnInit {    pluploadInterval:number = null;    hasPlupload: boolean = false;    ngOnInit() {        lazyload("https://cdnjs.cloudflare.com/ajax/libs/plupload/2.3.1/plupload.full.min.js");        this.pluploadInterval = window.setInterval(()=>{            if(window.plupload) { // you can check existence of plupload object any way you want                // Woohoo, I am ready to be used                this.hasPlupload = true; // everything is run outside ngZone, wrap it if angular2 is not reacting to changes, or change window.setInterval to setInterval                window.clearInterval(this.pluploadInterval); // don't forget to clean interval            }        }, 100); // timeinterval can vary ....

The browser will load this automatically.

Notice if(plupload) it assumes that there is global object plupload that the script adds (I do not know if it truely added, check your working example in pure javascript). As it is jquery extension you can check it's prototype like this: jQuery test for whether an object has a method?

OLD HISTORICAL:@Reid here is plunker: https://plnkr.co/edit/zDWWQbTQUSHBqCsrUMUi?p=preview the plupload is actually loaded, but added to require with define("plupload", ['./moxie'], extract); I am not sure at the moment how to extract from there and which package require is belong to... the code for finding correct module loader belongs to plupload itself, here it is (from plupload.dev.js):

if (typeof define === "function" && define.amd) {    define("plupload", ['./moxie'], extract);} else if (typeof module === "object" && module.exports) {    module.exports = extract(require('./moxie'));} else {    global.plupload = extract(global.moxie);}


I think that your best bet is to use the Require.js Library so that you can dynamically load your scripts from within your components.

The small trade off is that you will have to add this 18KB library to your index.html page (CDN), however this could save you huge amounts of loading if your 3rd party libraries are massive.

I have no experience with using plupload, so instead I put together the following plunkr which uses an external animation library, drawn from a CDN. The plunkr animates a number from 0 - 100.

https://plnkr.co/edit/fJCtezsERYHOYplLh7Jo?p=preview


index.html

<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.3/require.min.js"></script>

component.ts

ngOnInit(){    // Dynamically loads the framework from the CDN.    require(["https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenLite.min.js"],    // This function is then called ONCE LOAD IS COMPLETE    function (common) {      // Do greensock things      var demo = {score:0},      scoreDisplay = document.getElementById("scoreDisplay");      //create a tween that changes the value of the score property of the demo object from 0 to 100 over the course of 20 seconds.      var tween = TweenLite.to(demo, 20, {score:100, onUpdate:showScore})      //each time the tween updates this function will be called.      function showScore() {        scoreDisplay.innerHTML = demo.score.toFixed(2);      }      });  }

What I like about this approach, is that in the onLoad callback from require, the syntax is unchanged from a normal implementation of the library, so you can just copy paste your currently working code into the callback.