I am trying to use MathJax to display equations in my Angular 2
app. Only a small bit of my app actually needs this functionality, so I don't want all users to have to download the 3-400kb needed.
My initial thought was to import "mathjax"
within my ponent, so that the library will be loaded only when the ponent is created. However, I learned from my earlier question that MathJax doesn't play well with others because it uses its own custom module loader.
My current setup loads the script
from my index.html
but that means everyone will download it when only a small percentage of users need this ponent
<script src=".js?config=AM_SVG"></script>
Is there a clean way in Angular 2
to have my ponent download a script within the ngOnInit()
hook and execute a callback once it's in the DOM? Something such as (pseudo-code):
ngOnInit(){
//download script, add to DOM
fetch('https://...MathJax.js').then(addToDom).then(this.onMathJaxLoaded);
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
I am trying to use MathJax to display equations in my Angular 2
app. Only a small bit of my app actually needs this functionality, so I don't want all users to have to download the 3-400kb needed.
My initial thought was to import "mathjax"
within my ponent, so that the library will be loaded only when the ponent is created. However, I learned from my earlier question that MathJax doesn't play well with others because it uses its own custom module loader.
My current setup loads the script
from my index.html
but that means everyone will download it when only a small percentage of users need this ponent
<script src="https://cdn.mathjax/mathjax/latest/MathJax.js?config=AM_SVG"></script>
Is there a clean way in Angular 2
to have my ponent download a script within the ngOnInit()
hook and execute a callback once it's in the DOM? Something such as (pseudo-code):
ngOnInit(){
//download script, add to DOM
fetch('https://...MathJax.js').then(addToDom).then(this.onMathJaxLoaded);
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
Share
Improve this question
edited May 23, 2017 at 11:54
CommunityBot
11 silver badge
asked Dec 6, 2016 at 9:37
BeetleJuiceBeetleJuice
41k22 gold badges118 silver badges181 bronze badges
3
-
Might be a option to inject a
<script>
tag into your DOM.. stackoverflow./questions/16230886/… – TryingToImprove Commented Dec 6, 2016 at 9:39 - Note from the future: cdn.mathjax is nearing its end-of-life, check mathjax/cdn-shutting-down for migration tips. – Peter Krautzberger Commented Apr 11, 2017 at 15:35
- @PeterKrautzberger Thanks for the note – BeetleJuice Commented Apr 22, 2017 at 5:24
2 Answers
Reset to default 6I have not tried it, but I think this might be possible:
ngOnInit(){
//download script, add to DOM
var script = document.createElement('script');
document.body.appendChild(script)
script.onload = this.onMathJaxLoaded.bind(this);
script.src = 'https://...MathJax.js';
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
Here is a fiddle, which contains a example of how it would work in plain JavaScript; https://jsfiddle/5qu5h7bc/
Building on @TryingToImprove's solution, I decided to add a ScriptLoaderService
to my app with a load()
function that fetches a remote script and notifies the caller (using an RxJS Observable) when it's done.
Here is how to use it:
this.scriptLoaderService.load('https://...js').subscribe(
()=>console.log('script has loaded');
);
Here is the service:
/**
* A directory of scripts that have been or are being loaded
*/
private scripts = new Map<string,Observable<boolean>|boolean>();
/**
* Downloads script; returns Observable that emits TRUE once "load" event fires
*/
public load(src:string):Observable<boolean>{
if(this.scripts.has(src)){
// If script has already been fully loaded.
if(this.scripts.get(src)===true) return Observable.of(true);
// Else if download is already underway but load event hasn't fired yet
else return this.scripts.get(src);
}
//Add a script tag to the DOM
let script = document.createElement('script');
document.body.appendChild(script);
// upon subscription, listen for the load event. Once it arrives, emit TRUE
let obs:Observable<boolean> = Observable.fromEvent(script,'load')
// Map should hold TRUE once script is loaded
.do(()=> this.scripts.set(src,true))
.take(1).map(e=>true);
// set the src attribute (will cause browser to download)
script.src = src;
//add observable to the scripts Map
this.scripts.set(src,obs);
return obs;
}
Tested on Firefox 51