Created
July 3, 2020 23:46
-
-
Save LayZeeDK/5c30299be46ce5215947945d50506b87 to your computer and use it in GitHub Desktop.
Angular script service for loading JavaScript script files in the browser, using RxJS.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DOCUMENT } from '@angular/common'; | |
import { Inject, Injectable, Renderer2 } from '@angular/core'; | |
import { Observable, ReplaySubject } from 'rxjs'; | |
import { switchMapTo, tap } from 'rxjs/operators'; | |
@Injectable({ | |
providedIn: 'root', | |
}) | |
export class ScriptService { | |
private scriptLoaded = new Map<string, Observable<void>>(); | |
constructor( | |
@Inject(DOCUMENT) private document: Document, | |
private renderer: Renderer2 | |
) {} | |
load(url: string): Observable<void> { | |
if (this.scriptLoaded.has(url)) { | |
return this.scriptLoaded.get(url) as Observable<void>; | |
} | |
const loaded = new ReplaySubject<void>(1); | |
const loaded$ = loaded.asObservable(); | |
this.scriptLoaded.set(url, loaded$); | |
return this.loadScript(url).pipe( | |
tap({ | |
error: (error: ErrorEvent) => { | |
loaded.error(error); | |
}, | |
next: () => { | |
loaded.next(); | |
loaded.complete(); | |
}, | |
}), | |
switchMapTo(loaded$) | |
); | |
} | |
private loadScript(url: string): Observable<void> { | |
return new Observable((subscriber) => { | |
const script: HTMLScriptElement = this.renderer.createElement('script'); | |
script.type = 'application/javascript'; | |
script.async = true; | |
script.src = url; | |
const appendScript = () => { | |
this.renderer.appendChild(this.document.body, script); | |
}; | |
const removeScript = () => { | |
this.renderer.removeChild(this.document.body, script); | |
}; | |
const onLoad = () => { | |
teardown(); | |
subscriber.next(); | |
subscriber.complete(); | |
}; | |
const onError = (error: ErrorEvent) => { | |
teardown(); | |
subscriber.error(error); | |
}; | |
const removeOnLoad = this.renderer.listen(script, 'load', onLoad); | |
const removeOnError = this.renderer.listen(script, 'error', onError); | |
let isDestroyed = false; | |
const teardown = () => { | |
if (isDestroyed) { | |
return; | |
} | |
removeScript(); | |
removeOnLoad(); | |
removeOnError(); | |
isDestroyed = true; | |
}; | |
appendScript(); | |
return teardown; | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment