4

I'm using an external library that attaches itself to the global window object (window['lib']) once the library's javascript file is loaded by the browser. I'm trying to invoke code using this library whenever a component is loaded, however everytime I try to access the object it is undefined (due to the library not having been loaded). I have tried every lifecycle hook I can think of, however nothing appears to wait for the DOM to be fully ready. For example, I want to do something like this:

ngOnInit() {
    window['lib'].doStuff(); // <-- window['lib'] is undefined
}

If I wrap it in a timeout, then it becomes available. However, this looks like code smell and do not want to approach it this way:

ngOnInit() {
    setTimeout(function() {
        window['lib'].doStuff(); // <-- this works
    });
}

What is the best / suggested / "most angular way" to approach this problem? Thanks!

ossys
  • 4,157
  • 5
  • 32
  • 35
  • If the library is loaded before main.js, then it should be available (as long as async/defer attributes are not specified). If you are using angular cli, try including the script in your – Brian Jun 27 '18 at 23:58
  • 1
    I use the same to force some things to work, because the DOM may be not fully ready on both `OnInit` and `AfterViewInit`. Not sure that there is another way. The only thing I can suggest to try is to use `ngAfterContentInit`. – P.S. Jun 28 '18 at 00:07

1 Answers1

2

Angular Lifecycle Hook: ngOnInit()

Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.

Called once, after the first ngOnChanges().

This is a common problem with Angular. Older methodologies like this one that uses a global variable on the window object will piss off the way Angular loads the application and also TypeScript (during development). The reason why you have to do window['lib'] and not window.lib is because TypeScript doesn't know anything about the types of window.lib so window['lib'] is a way to force it to work.

The other part is that depending on what type of compilation you're using (AOT vs JIT), that library you're loading may or may not be ready yet (also depends on how you're loading that script/module into the application). As Commercial Suicide mentioned, you could try some of the other Angular Lifecycle Hooks but more than likely, you're going to end up settling on setTimeout. You actually don't even need to define the timeout period or you can pass 0ms (as you've done in your code). Angular just wants you to hold off on calling that function until the DOM it's finished rendering.

I personally can't wait until all the jQuery-like libraries are converted into proper ES6 modules. The days of just throwing a script tag at the bottom of the body are long gone.

mwilson
  • 12,295
  • 7
  • 55
  • 95
  • thanks for the links! I had also tried creating a service to inject the window object into my component, but the library is still unavailable by the time the lifecycle hooks are called. – ossys Jun 28 '18 at 14:13
  • Yes. One thing you could try is importing the `NgZone` module and calling the `.run` function. This is pretty equivalent to angularjs's `.$apply` function. Not sure if it would work as I haven't tried it. I usually just settle on `setTImeout` and hope that whatever library I'm using will eventually figure out a better way for developers to use their library. Please mark as answer as this seems to provide you with the information needed to solve your problem. – mwilson Jun 28 '18 at 15:16