Parfois on arrive sur des cas où on a besoin de faire des choses assez borderline en termes de chargement comme charger dynamiquement un script depuis un composant ou un service. Dans ces cas-là rien n'est vraiment prévu en Angular pour faire ça, car ça n'est clairement pas une bonne habitude aussi !
Ici je vous montre une solution que j'ai créé en assemblant pas mal de petites astuces glanées çà et là sur Internet au fil du temps pour arriver à une solution la plus propre possible.
import { Injectable, Renderer2, RendererFactory2 } from '@angular/core';
const LOADED_SCRIPTS: string[] = [];
@Injectable()
export class ScriptLoaderService {
private renderer: Renderer2;
constructor(rendererFactory: RendererFactory2) {
this.renderer = rendererFactory.createRenderer(null, null);
}
loadScript(url: string): void {
if (!LOADED_SCRIPTS.includes(url) && typeof document?.head !== 'undefined') {
const scriptTag = this.renderer.createElement('script');
this.renderer.setProperty(scriptTag, 'src', url);
this.renderer.appendChild(document.head, scriptTag);
LOADED_SCRIPTS.push(url);
}
}
}
Comment ça fonctionne ?
Dans le constructeur on s'injecte un RendererFactory2 qui va nous permettre de créer un Renderer2 qui pointe vers rien du tout car on ne veut pas référencer de composant (c'est pour ça les null en paramètre).
Au moment de charger un script, on appelle la fonction loadScript. Celle-ci va commencer par vérifier que le script n'a pas déjà été chargé via un tableau en dehors du scope de la classe pour éviter de dépendre de l'instance de classe. S'il n'a pas déjà été chargé, on crée une balise script avec un src définie avec l'url indiquée et on l'ajoute dans les balises head de notre document.
Je passe la vérification de l'existence de document.head qui permet d'éviter des erreurs à la marge.