Angular2: how to load external javascript file dynamically

I am trying to load an external library (Javascript) to an Angular2 component (TypeScript).

Take example: https://github.com/fengyuanchen/cropperjs

index.html

<head>
   <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.css" rel="stylesheet">
   <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.js"></script>
</head>

myCropper.js

window.addEventListener('DOMContentLoaded', function () {
 var image = document.querySelector('#image');
 var cropper = new Cropper(image, {
    viewMode: 3,
    dragMode: 'move',
    autoCropArea: 1,
    restore: false,
    modal: false,
    guides: false,
    highlight: false,
    cropBoxMovable: false,
    cropBoxResizable: false,
    toggleDragModeOnDblclick: false,
  });
 });

photo.component.ts

@Component({
selector: 'photo-crop',
template: `
    <div class="row">
      <img id="image" src="http://img01.ibnlive.in/ibnlive/uploads/875x584/jpg/2015/09/new-google-logo-010915.png" class="img-responsive"/>
    <div class="row">
    `,
styles: []
})
export class PhotoComponent {

public ngOnInit() {
    this.loadScript('src/assets/js/myCropper.js');           
    });
}

public loadScript(url) {
    console.log('preparing to load...')
    let node = document.createElement('script');
    node.src = url;
    node.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(node);
 }
}

Result: the image is loaded without zoom/crop effect. I inspected the page and saw the script was added correctly in . I refreshed page, no luck. I had no error at all. It seems Angular2 does not activate the script.

Any suggestion ?

Hi @peterpastor84, by the time you added that script, the DOMContentLoaded event was long triggered, so the callback won’t ever execute. You might just put that code inside an IIFE instead.

I’d suggest not to place the loadScript call in ngOnInit though, as you might end up with the script being added several times. It would make more sense to export that myCropper code as a function (like the one that is currently your event callback) from a dedicated module, which you’d then simply import and call on init – no messing with script elements in the document head.

Hi, thanks m3g4p0p for your fast reply. To be honest, I am a fresher in Javascript. Following your suggestion, I updated my code as below:

MyCropper.ts

declare var Cropper:any;

export class MyCropper {
  constructor(
){};

  foo: string = (function crop(){
    var image = document.querySelector('#image');
    var cropper = new Cropper(image, {
      viewMode: 3,
      dragMode: 'move',
      autoCropArea: 1,
      restore: false,
      modal: false,
      guides: false,
      highlight: false,
      cropBoxMovable: false,
      cropBoxResizable: false,
      toggleDragModeOnDblclick: false,
    });

    console.log("loaded Script");
    
    return "value";
  }());
}

photo.component.ts

import {MyCropper} from "../../util/MyCropper";
....
ngOnInit() {

new MyCropper();
}

Result: It still doesn’t work. Please correct me if I am wrong. Thank you for your time again.

You’re welcome. :-) That foo property is not right here, if at all it should be a method so that you can call it when it’s needed. (The IIFE would just be relevant to a dedicated script as in your OP, sorry for the confusion!) However I’d just export the function, no need for a class here… like

cropper.ts

export default function crop (): void {
  var image = document.querySelector('#image')
  // etc...
  // BTW, why return the string "value"?
}

photo.component.ts

import crop from './cropper'

// ...
ngOnInit (): void {
  crop()
}

Hi m3g4p0p, I updated my code as your example. Unfortunately, It still doesn’t work … image is loaded without zoom/crop. :frowning:

Then there would be a problem with the crop function itself… the lifecycle hook and all should be right. Does the crop function work by itself (i.e. outside of angular)? Are there any errors in the console?

Hi m3g4p0p,
I tried to add the script directly to index.html

<head>
   <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.css" rel="stylesheet">
   <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.js"></script>
   <script type="text/javascript">
    window.addEventListener('DOMContentLoaded', function () {
      var image = document.querySelector('#image');
      var cropper = new Cropper(image, {
        viewMode: 3,
        dragMode: 'move',
        autoCropArea: 1,
        restore: false,
        modal: false,
        guides: false,
        highlight: false,
        cropBoxMovable: false,
        cropBoxResizable: false,
        toggleDragModeOnDblclick: false,
      });
    });

  </script>

</head>

It doesn’t work at start, but after I refresh the page, it worked.
You know that it’s not a good practice, and it also doesn’t work properly.

I did use this library outside Angular, it’s fine, no problem at all.

Hi @m3g4p0p,
I spent my whole weekend to play with my issue, and I found the solution. As your suggestion, the lifecycle hook is the reason, I am new in Angular2, so it took me quite long to understand them.

Solution: call crop() in ngAfterViewInit instead of ngOnInit

ngAfterViewInit(): void {
    crop();
  }

Bam!! It worked as a charm.
Thank you for your support, m3g4p0p

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.