web-dev-qa-db-fra.com

Charger dynamiquement un fichier javascript externe à partir du composant Angular

Je crée une application angulaire utilisant Angular 4 et le CLI. J'essaie d'ajouter le widget de recherche SkyScanner à l'un de mes composants.

Exemple de widget Skyscanner

Une partie de l'implémentation nécessite l'ajout d'un nouveau script externe:

<script src="https://widgets.skyscanner.net/widget-server/js/loader.js" async></script>

Je ne suis pas sûr de la manière correcte de référencer ce fichier. Si j'ajoute le script dans mon fichier index.html, le widget ne se charge que si une actualisation complète de la page est effectuée. Je suppose que le script tente de manipuler le DOM lors du chargement et que les éléments n'existent pas lors de l'exécution du script.

Quelle est la bonne façon de charger le script uniquement lorsque le composant contenant le widget Skyscanner est chargé?

25
psych

Essayez de charger du code JavaScript externe lors du chargement des composants, comme indiqué ci-dessous:

loadAPI: Promise<any>;

constructor() {        
    this.loadAPI = new Promise((resolve) => {
        this.loadScript();
        resolve(true);
    });
}

public loadScript() {        
    var isFound = false;
    var scripts = document.getElementsByTagName("script")
    for (var i = 0; i < scripts.length; ++i) {
        if (scripts[i].getAttribute('src') != null && scripts[i].getAttribute('src').includes("loader")) {
            isFound = true;
        }
    }

    if (!isFound) {
        var dynamicScripts = ["https://widgets.skyscanner.net/widget-server/js/loader.js"];

        for (var i = 0; i < dynamicScripts .length; i++) {
            let node = document.createElement('script');
            node.src = dynamicScripts [i];
            node.type = 'text/javascript';
            node.async = false;
            node.charset = 'utf-8';
            document.getElementsByTagName('head')[0].appendChild(node);
        }

    }
}

J'ai eu le même problème, mais dans mon cas, j'importais 10 bibliothèques à la fin du fichier html, et ces bibliothèques ont beaucoup de méthodes, d'écouteurs, d'événements et plus, et dans mon cas, je n'avais pas besoin appeler une méthode spécifiquement.

L'exemple de ce que j'ai eu:

<!-- app.component.html -->

<div> 
 ...
</div>

<script src="http://www.some-library.com/library.js">
<script src="../assets/js/my-library.js"> <!-- a route in my angular project -->

Comme mentionné, cela n'a pas fonctionné. Ensuite, je trouve quelque chose qui m'a aidé: Réponse de Milad

  1. Supprimez les appels de script dans le fichier app.component.html. Vous devez lier ces scripts dans le fichier app.component.ts.

  2. Dans ngOnInit (), utilisez une méthode pour ajouter les bibliothèques, par exemple:

``

<!-- app.component.ts -->

export class AppComponent implements OnInit {
   title = 'app';
   ngOnInit() {
     this.loadScript('http://www.some-library.com/library.js');
     this.loadScript('../assets/js/my-library.js');
   }
  }

  public loadScript(url: string) {
    const body = <HTMLDivElement> document.body;
    const script = document.createElement('script');
    script.innerHTML = '';
    script.src = url;
    script.async = false;
    script.defer = true;
    body.appendChild(script);
  }
}

Cela fonctionne pour moi. J'utilise Angular 6, j'espère que ça aide.

12
William Cuervo

J'ai fait cet extrait de code

 addJsToElement(src: string): HTMLScriptElement {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = src;
    this.elementRef.nativeElement.appendChild(script);
    return script;
  }

Et puis appelez comme ça

this.addJsToElement('https://widgets.skyscanner.net/widget-server/js/loader.js').onload = () => {
        console.log('SkyScanner Tag loaded');
}

EDIT: Avec le nouveau moteur de rendu Api, il peut être écrit comme ceci

constructor(private renderer: Renderer2){}

 addJsToElement(src: string): HTMLScriptElement {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = src;
    this.renderer.appendChild(document.body, script);
    return script;
  }

StackBlitz

5
Eduardo Vargas

ajoutez loader.js à votre dossier de ressources, puis dans votre angular-cli.json

"scripts": ["./src/assets/loader.js",]

puis ajoutez ceci à votre typings.d.ts

 declare var skyscanner:any;

et vous pourrez l'utiliser 

  skyscanner.load("snippets","2");
5
Hamed Baatour

Vous pouvez créer votre propre directive pour charger le script comme ci-dessous

import { Directive, OnInit, Input } from '@angular/core';

@Directive({
    selector: '[appLoadScript]'
})
export class LoadScriptDirective implements OnInit{

    @Input('script') param:  any;

    ngOnInit() {
        let node = document.createElement('script');
        node.src = this.param;
        node.type = 'text/javascript';
        node.async = false;
        node.charset = 'utf-8';
        document.getElementsByTagName('head')[0].appendChild(node);
    }

}

Et vous pouvez l'utiliser n'importe où dans votre modèle de composants, comme indiqué ci-dessous 

<i appLoadScript  [script]="'script_file_path'"></i>

Par exemple, pour charger dynamiquement JQuery dans votre composant, insérez le code ci-dessous dans le modèle de votre composant.

<i appLoadScript  [script]="'/assets/baker/js/jquery.min.js'"></i>
1
Anil Agrawal

Remarque: Ceci est spécialement destiné aux liens js externes! Étape 1. Il est recommandé d'ajouter votre script angulaire au fichier index.html situé au bas de votre corps! J'ai essayé tous les autres moyens mais j'ai échoué.

<!-- File Name: index.html and its inside src dir-->

<body class="">
  <app-root></app-root>

    <!-- Icons -->
        <script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script>

</body>

Ensuite, il y a deux façons de faire cela ... Dans Anguar5, utilisez dans le dossier de votre composant en haut, tapez dans ce code

declare var feather:any;

dans votre classe, appelez la méthode dont vous avez besoin. Par exemple

//FileName: dashboard.component.ts
import { Component, OnInit } from '@angular/core';
declare var feather:any;
export class DashboardComponent implements OnInit{
    ngOnInit(){
        feather.replace();
    }
}

et cela devrait exécuter votre code! L’autre façon qui pourrait fonctionner dans une version plus ancienne. Je n'ai pas vérifié!

//FileName: dashboard.component.ts
import { Component, OnInit } from '@angular/core';

export class DashboardComponent implements OnInit{

     ngOnInit(){
    
    
        let node = document.createElement('script');
        node.innerText='feather.replace()';
        node.type = 'text/javascript';
        node.async = false;
        node.charset = 'utf-8';
        
        document.getElementsByTagName('body')[0].appendChild(node);
    
    }

}

Si vous n'obtenez pas mon code, essayez également ceci link .

J'espère que cela t'aides!

0
3rdi

Tu peux faire une chose 

si vous avez angular-cli.json

alors vous pouvez déclarer au script

comme

"scripts": ["../src/assets/js/loader.js"]

Et puis déclarez skyscanner dans votre composant

comme 

declare var skyscanner:any;

C'est tout!

J'espère que cela vous aidera

0
Jalay Oza

Un peu tard mais je préfère faire de cette façon (service moyen) ....

import { Injectable } from '@angular/core';
import { Observable } from "rxjs";

interface Scripts {
  name: string;
  src: string;
}

export const ScriptStore: Scripts[] = [
  { name: 'script-a', src: 'assets/js/a.js' },
  { name: 'script-b', src: 'assets/js/b.js' },
  { name: 'script-c', src: 'assets/js/c.js' }
];

declare var document: any;

@Injectable()
export class FileInjectorService {

  private scripts: any = {};

  constructor() {
    ScriptStore.forEach((script: any) => {
      this.scripts[script.name] = {
        loaded: false,
        src: script.src
      };
    });
  }

  loadJS(...scripts: string[]) {
    const promises: any[] = [];
    scripts.forEach((script) => promises.Push(this.loadJSFile(script)));
    return Promise.all(promises);
  }

  loadJSFile(name: string) {
    return new Promise((resolve, reject) => {
      if (!this.scripts[name].loaded) {
        let script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = this.scripts[name].src;
        if (script.readyState) {
            script.onreadystatechange = () => {
                if (script.readyState === "loaded" || script.readyState === "complete") {
                    script.onreadystatechange = null;
                    this.scripts[name].loaded = true;
                    resolve({script: name, loaded: true, status: 'Loaded'});
                }
            };
        } else {
            script.onload = () => {
                this.scripts[name].loaded = true;
                resolve({script: name, loaded: true, status: 'Loaded'});
            };
        }
        script.onerror = (error: any) => resolve({script: name, loaded: false, status: 'Loaded'});
        document.getElementsByTagName('head')[0].appendChild(script);
      } else {
        resolve({ script: name, loaded: true, status: 'Already Loaded' });
      }
    });
  }

}

Ensuite, dans ma composante, je pourrais faire quelque chose comme:

ngOnInit() {
  this.fileInjectorService.loadJS('script-a', 'script-c').then(data => {
    // Loaded A and C....
  }).catch(error => console.log(error));
}

Testé en angulaire 6/7

0
carlitoxenlaweb