Bagaimana cara memuat skrip eksternal secara dinamis di Angular?


151

Saya memiliki modul ini yang memompilisasi perpustakaan eksternal bersama dengan logika tambahan tanpa menambahkan <script>tag langsung ke index.html:

import 'http://external.com/path/file.js'
//import '../js/file.js'

@Component({
    selector: 'my-app',
    template: `
        <script src="http://iknow.com/this/does/not/work/either/file.js"></script>
        <div>Template</div>`
})
export class MyAppComponent {...}

Saya melihat bahwa importdengan ES6 spec statis dan diselesaikan selama pengalihan TypeScript daripada saat runtime.

Pokoknya untuk membuatnya dapat dikonfigurasi sehingga file.js akan memuat baik dari CDN atau folder lokal? Bagaimana cara memberitahu Angular 2 untuk memuat skrip secara dinamis?


Jawaban:


61

Jika Anda menggunakan system.js, Anda dapat menggunakan System.import()saat runtime:

export class MyAppComponent {
  constructor(){
    System.import('path/to/your/module').then(refToLoadedModule => {
      refToLoadedModule.someFunction();
    }
  );
}

Jika Anda menggunakan webpack, Anda dapat memanfaatkan sepenuhnya dukungan pemisahan kode yang kuat dengan require.ensure:

export class MyAppComponent {
  constructor() {
     require.ensure(['path/to/your/module'], require => {
        let yourModule = require('path/to/your/module');
        yourModule.someFunction();
     }); 
  }
}

1
Sepertinya saya perlu menggunakan pemuat modul khusus dalam komponen. Yah, itu baik karena itu Systemadalah masa depan loader, saya pikir.
CallMeLaNN

6
TypeScript Plugin for Sublime Texttidak senang dengan Systemdalam kode TypeScript: Cannot find name 'System'tetapi tidak ada kesalahan selama pengubahan dan pengoperasian. File skrip Angular2dan keduanya Systemsudah ditambahkan index.html. Pokoknya untuk importyang Systemdan membuat bahagia Plugin?
CallMeLaNN

1
tetapi bagaimana jika Anda tidak menggunakan system.js!
Murhaf Sousli

2
@drewmoore Saya tidak ingat mengapa saya bilang begitu, require()/ importharus bekerja dengan baik sebagai jawaban Anda sekarang, 1
Murhaf Sousli

9
Dari apa yang saya tahu opsi ini tidak berfungsi untuk skrip di internet, hanya untuk file lokal. Ini sepertinya tidak menjawab pertanyaan awal seperti yang ditanyakan.
WillyC

140

Anda dapat menggunakan teknik berikut untuk secara dinamis memuat skrip JS dan perpustakaan sesuai permintaan di proyek Angular Anda.

script.store.ts akan berisi lintasan skrip secara lokal atau pada server jarak jauh dan nama yang akan digunakan untuk memuat skrip secara dinamis

 interface Scripts {
    name: string;
    src: string;
}  
export const ScriptStore: Scripts[] = [
    {name: 'filepicker', src: 'https://api.filestackapi.com/filestack.js'},
    {name: 'rangeSlider', src: '../../../assets/js/ion.rangeSlider.min.js'}
];

script.service.ts adalah layanan injeksi yang akan menangani pemuatan skrip, salin script.service.tsapa adanya

import {Injectable} from "@angular/core";
import {ScriptStore} from "./script.store";

declare var document: any;

@Injectable()
export class ScriptService {

private scripts: any = {};

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

load(...scripts: string[]) {
    var promises: any[] = [];
    scripts.forEach((script) => promises.push(this.loadScript(script)));
    return Promise.all(promises);
}

loadScript(name: string) {
    return new Promise((resolve, reject) => {
        //resolve if already loaded
        if (this.scripts[name].loaded) {
            resolve({script: name, loaded: true, status: 'Already Loaded'});
        }
        else {
            //load script
            let script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = this.scripts[name].src;
            if (script.readyState) {  //IE
                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 {  //Others
                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);
        }
    });
}

}

Suntikkan ini di ScriptServicemana pun Anda membutuhkannya dan muat js libs seperti ini

this.script.load('filepicker', 'rangeSlider').then(data => {
    console.log('script loaded ', data);
}).catch(error => console.log(error));

7
Ini bekerja dengan sangat baik. Mencukur lebih dari 1MB dari ukuran muat awal - Saya punya banyak perpustakaan!
Our_Benefactors

2
Sepertinya saya berbicara terlalu cepat. Solusi ini TIDAK berfungsi di iOS Safari.
Our_Benefactors

setelah memuat skrip, periksa apakah itu menambahkan ke kepala dom Anda
Rahul Kumar

Mungkin saya tidak menyuntikkan dengan benar, tetapi saya mendapatkanthis.script.load is not a function
Towelie

Terima kasih. Ini berfungsi, tetapi saya mendapatkan skrip saya (animasi latar belakang) dimuat di setiap komponen lainnya. Saya ingin dimuat hanya pada komponen pertama saya (ini adalah halaman login). Apa yang bisa saya lakukan untuk mencapainya? Terima kasih
Joel Azevedo

47

Ini mungkin berhasil. Kode ini secara dinamis menambahkan <script>tag ke headfile html pada tombol yang diklik.

const url = 'http://iknow.com/this/does/not/work/either/file.js';

export class MyAppComponent {
    loadAPI: Promise<any>;

    public buttonClicked() {
        this.loadAPI = new Promise((resolve) => {
            console.log('resolving promise...');
            this.loadScript();
        });
    }

    public loadScript() {
        console.log('preparing to load...')
        let node = document.createElement('script');
        node.src = url;
        node.type = 'text/javascript';
        node.async = true;
        node.charset = 'utf-8';
        document.getElementsByTagName('head')[0].appendChild(node);
    }
}

1
Manusia Thanx Bekerja untuk saya. Anda sekarang dapat memuatnya di halaman memuat juga menggunakan metode ngonit. Jadi klik tombol tidak diperlukan.
Niraj

Saya menggunakan cara yang sama .. tetapi tidak berhasil untuk saya. bisakah kamu membantu saya untuk hal yang sama ?? this.loadJs ("jalur javascriptfile"); loadjs publik (file) {let node = document.createElement ('script'); node.src = file; node.type = 'teks / javascript'; node.async = true; node.charset = 'utf-8'; document.getElementsByTagName ('head') [0] .appendChild (node); }
Mitesh Shah

Apakah Anda mencoba melakukannya pada klik atau pada pemuatan halaman? Bisakah Anda memberi tahu kami lebih lanjut tentang hasil atau kesalahan?
ng-darren

halo, saya telah mencoba menerapkan ini dan bekerja dengan baik tetapi new Promisetidak terselesaikan. Bisakah Anda memberi tahu saya Promiseapakah saya harus mengimpor sesuatu?
user3145373 ツ

Hai, saya tidak mengimpor sesuatu yang istimewa ketika saya menggunakan Promise.
ng-darren

23

Saya telah memodifikasi jawaban @rahul kumars, sehingga ia menggunakan Observables sebagai gantinya:

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

@Injectable()
export class ScriptLoaderService {
    private scripts: ScriptModel[] = [];

    public load(script: ScriptModel): Observable<ScriptModel> {
        return new Observable<ScriptModel>((observer: Observer<ScriptModel>) => {
            var existingScript = this.scripts.find(s => s.name == script.name);

            // Complete if already loaded
            if (existingScript && existingScript.loaded) {
                observer.next(existingScript);
                observer.complete();
            }
            else {
                // Add the script
                this.scripts = [...this.scripts, script];

                // Load the script
                let scriptElement = document.createElement("script");
                scriptElement.type = "text/javascript";
                scriptElement.src = script.src;

                scriptElement.onload = () => {
                    script.loaded = true;
                    observer.next(script);
                    observer.complete();
                };

                scriptElement.onerror = (error: any) => {
                    observer.error("Couldn't load script " + script.src);
                };

                document.getElementsByTagName('body')[0].appendChild(scriptElement);
            }
        });
    }
}

export interface ScriptModel {
    name: string,
    src: string,
    loaded: boolean
}

1
Anda lupa mengatur asyncuntuk true.
Will Huang

Saya punya pertanyaan, ketika kita menambahkan skrip dan menavigasi antara rute, katakanlah saya memuat skrip di rumah dan menjelajah sekitar dan kembali ke rumah, apakah ada cara untuk menginstal kembali skrip saat ini? Berarti saya ingin menghapus yang dimuat sebelumnya dan memuatnya lagi? Terima kasih banyak
d123546

@ d123546 Ya, jika Anda ingin itu mungkin. Tetapi Anda harus menulis beberapa logika yang menghapus tag skrip. Lihat di sini: stackoverflow.com/questions/14708189/…
Joel

Terima kasih Joel, tetapi apakah ada cara untuk menghapus fungsi yang diinisialisasi oleh skrip? Saat ini saya menghadapi masalah dalam proyek angular4 saya, karena saya memuat skrip yang menginisialisasi slider jquery, jadi ketika rute pertama dimuat dengan benar, tetapi kemudian ketika saya menavigasi ke rute lain dan kembali ke rute saya, skrip dimuat dua kali dan slider tidak memuat lagi :( Bisakah Anda memberi tahu saya tentang hal ini
d123546

1
Ada kesalahan di telepon private scripts: {ScriptModel}[] = [];. Seharusnyaprivate scripts: ScriptModel[] = [];
Chris Haines

20

Namun pilihan lain adalah menggunakan scriptjspaket untuk hal itu

memungkinkan Anda memuat sumber daya skrip sesuai permintaan dari URL apa pun

Contoh

Instal paket:

npm i scriptjs

dan ketik definisi untukscriptjs :

npm install --save @types/scriptjs

Kemudian $script.get()metode impor :

import { get } from 'scriptjs';

dan akhirnya memuat sumber skrip, dalam perpustakaan Google Maps kasus kami:

export class AppComponent implements OnInit {
  ngOnInit() {
    get("https://maps.googleapis.com/maps/api/js?key=", () => {
        //Google Maps library has been loaded...
    });
  }
}

Demo


Terima kasih telah berbagi tetapi bagaimana cara menambahkan properti tambahan dengan url seperti async defer ?? Bisakah Anda membimbing saya cara memuat skrip ini menggunakan pustaka di atas? <script type = 'text / javascript' src = ' bing.com/api/maps/mapcontrol?branch=release ' async defer> </script>
Pushkar Rathod

Karena baris subjeknya sama, saya hanya menjawab di sini
Pushkar Rathod

Hebat, tetapi alih-alih npm instal --save @ types / scriptjs, ini seharusnya npm instal --save-dev @ types / scriptjs (atau hanya npm i -D @ types / scriptjs)
Youness Houdass

18

Anda dapat memuat beberapa skrip secara dinamis seperti ini di component.tsfile Anda :

 loadScripts() {
    const dynamicScripts = [
     'https://platform.twitter.com/widgets.js',
     '../../../assets/js/dummyjs.min.js'
    ];
    for (let i = 0; i < dynamicScripts.length; i++) {
      const 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);
    }
  }

dan memanggil metode ini di dalam konstruktor,

constructor() {
    this.loadScripts();
}

Catatan: Agar skrip lainnya dimuat secara dinamis, tambahkan skrip ke dynamicScriptsarray.


3
Saya harus mengatakan, ini terlihat sederhana tetapi berfungsi dengan baik, sangat mudah diimplementasikan :-)
Pierre

3
Masalah dengan jenis injeksi skrip ini adalah karena Angular adalah SPA, skrip ini pada dasarnya tetap berada di cache browser bahkan setelah Anda menghapus tag skrip dari DOM.
j4rey

1
Jawaban yang luar biasa! Menyelamatkan hari saya, Ini bekerja dengan Angular 6 & 7. Diuji!
Nadeeshan Herath

Untuk mengirim data ke js eksternal dan dan mendapatkan data dari js eksternal, Panggil fungsi ini di app.component.ts dan gunakan mendeklarasikan var d3Sankey: apa saja di komponen modul tingkat fitur apa pun. Bekerja.
gnganpath

5

Saya telah melakukan potongan kode ini dengan api penyaji baru

 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;
  }

Dan kemudian menyebutnya seperti ini

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

StackBlitz


4

Saya memiliki cara yang baik untuk memuat skrip secara dinamis! Sekarang saya menggunakan ng6, echarts4 (> 700Kb), ngx-echarts3 dalam proyek saya. ketika saya menggunakannya oleh ngx-echarts's docs, saya perlu mengimpor echarts di angular.json: "scripts": ["./ node_modules / echarts / dist / echarts.min.js"] sehingga dalam modul login, halaman saat memuat skrip .js, ini file besar! Saya tidak menginginkannya.

Jadi, saya pikir sudut memuat setiap modul sebagai file, saya dapat memasukkan resolver router untuk preload js, kemudian mulai memuat modul!

// PreloadScriptResolver.service.js

/**动态加载js的服务 */
@Injectable({
  providedIn: 'root'
})
export class PreloadScriptResolver implements Resolve<IPreloadScriptResult[]> {
  // Here import all dynamically js file
  private scripts: any = {
    echarts: { loaded: false, src: "assets/lib/echarts.min.js" }
  };
  constructor() { }
  load(...scripts: string[]) {
    const promises = scripts.map(script => this.loadScript(script));
    return Promise.all(promises);
  }
  loadScript(name: string): Promise<IPreloadScriptResult> {
    return new Promise((resolve, reject) => {
      if (this.scripts[name].loaded) {
        resolve({ script: name, loaded: true, status: 'Already Loaded' });
      } else {
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = this.scripts[name].src;
        script.onload = () => {
          this.scripts[name].loaded = true;
          resolve({ script: name, loaded: true, status: 'Loaded' });
        };
        script.onerror = (error: any) => reject({ script: name, loaded: false, status: 'Loaded Error:' + error.toString() });
        document.head.appendChild(script);
      }
    });
  }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<IPreloadScriptResult[]> {
   return this.load(...route.routeConfig.data.preloadScripts);
  }
}

Kemudian di submodule-routing.module.ts , impor PreloadScriptResolver ini:

const routes: Routes = [
  {
    path: "",
    component: DashboardComponent,
    canActivate: [AuthGuardService],
    canActivateChild: [AuthGuardService],
    resolve: {
      preloadScripts: PreloadScriptResolver
    },
    data: {
      preloadScripts: ["echarts"]  // important!
    },
    children: [.....]
}

Kode ini berfungsi dengan baik, dan menjanjikan bahwa: Setelah file js dimuat, maka modul mulai memuat! Penyelesai ini dapat digunakan di banyak router


Bisakah Anda membagikan kode antarmuka IPreloadScriptResult?
Mehul Bhalala

3

Solusi universal sudut; Saya perlu menunggu elemen tertentu berada di halaman sebelum memuat skrip untuk memutar video.

import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from "@angular/common";

@Injectable({
  providedIn: 'root'
})
export class ScriptLoaderService {

  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
  ) {
  }

  load(scriptUrl: string) {
    if (isPlatformBrowser(this.platformId)) {
      let node: any = document.createElement('script');
      node.src = scriptUrl;
      node.type = 'text/javascript';
      node.async = true;
      node.charset = 'utf-8';
      document.getElementsByTagName('head')[0].appendChild(node);
    }
  }
}

1
Ini entah bagaimana terasa cerdik
Mustafa

2

Solusi @ rahul-kumar bekerja baik untuk saya, tetapi saya ingin memanggil fungsi javascript saya dalam naskah saya

foo.myFunctions() // works in browser console, but foo can't be used in typescript file

Saya memperbaikinya dengan mendeklarasikannya dalam naskah saya:

import { Component } from '@angular/core';
import { ScriptService } from './script.service';
declare var foo;

Dan sekarang, saya bisa menelepon foo di mana saja di file typecript saya


jika saya mengimpor beberapa skrip foo, foo1, foo2 dan saya hanya tahu selama waktu eksekusi bagaimana melakukan deklarasi secara dinamis?
natrayan

2

Dalam kasus saya, saya telah memuat file jsdan css visjsmenggunakan teknik di atas - yang bekerja sangat baik. Saya memanggil fungsi baru daringOnInit()

Catatan: Saya tidak bisa memuatnya dengan hanya menambahkan<script>dan<link>tag ke file template html.

loadVisJsScript() {
  console.log('Loading visjs js/css files...');
  let script = document.createElement('script');
  script.src = "../../assets/vis/vis.min.js";
  script.type = 'text/javascript';
  script.async = true;
  script.charset = 'utf-8';
  document.getElementsByTagName('head')[0].appendChild(script);    
	
  let link = document.createElement("link");
  link.type = "stylesheet";
  link.href = "../../assets/vis/vis.min.css";
  document.getElementsByTagName('head')[0].appendChild(link);    
}


Saya ingin memuat file CSS dari sumber web eksternal. Saat ini, saya menghadapi kesalahan yang mengatakan "Tidak diizinkan memuat sumber daya lokal" .. Harap sarankan.
Karan

Saya mencoba cara ini. Meskipun bekerja dengan baik untuk file Script, itu tidak bekerja untuk Stylesheets
118218

2

Hai Anda dapat menggunakan Renderer2 dan elementRef hanya dengan beberapa baris kode:

constructor(private readonly elementRef: ElementRef,
          private renderer: Renderer2) {
}
ngOnInit() {
 const script = this.renderer.createElement('script');
 script.src = 'http://iknow.com/this/does/not/work/either/file.js';
 script.onload = () => {
   console.log('script loaded');
   initFile();
 };
 this.renderer.appendChild(this.elementRef.nativeElement, script);
}

yang onloadfungsi dapat digunakan untuk memanggil fungsi script setelah script dimuat, ini sangat berguna jika Anda harus melakukan panggilan di ngOnInit ()


1

@ d123546 Saya menghadapi masalah yang sama dan mulai bekerja sekarang menggunakan ngAfterContentInit (Lifecycle Hook) pada komponen seperti ini:

import { Component, OnInit, AfterContentInit } from '@angular/core';
import { Router } from '@angular/router';
import { ScriptService } from '../../script.service';

@Component({
    selector: 'app-players-list',
    templateUrl: './players-list.component.html',
    styleUrls: ['./players-list.component.css'],
    providers: [ ScriptService ]
})
export class PlayersListComponent implements OnInit, AfterContentInit {

constructor(private router: Router, private script: ScriptService) {
}

ngOnInit() {
}

ngAfterContentInit() {
    this.script.load('filepicker', 'rangeSlider').then(data => {
    console.log('script loaded ', data);
    }).catch(error => console.log(error));
}

1
import { Injectable } from '@angular/core';
import * as $ from 'jquery';

interface Script {
    src: string;
    loaded: boolean;
}

@Injectable()
export class ScriptLoaderService {
    public _scripts: Script[] = [];

    /**
     * @deprecated
     * @param tag
     * @param {string} scripts
     * @returns {Promise<any[]>}
     */
    load(tag, ...scripts: string[]) {
        scripts.forEach((src: string) => {
            if (!this._scripts[src]) {
                this._scripts[src] = { src: src, loaded: false };
            }
        });

        const promises: any[] = [];
        scripts.forEach(src => promises.push(this.loadScript(tag, src)));

        return Promise.all(promises);
    }

    /**
     * Lazy load list of scripts
     * @param tag
     * @param scripts
     * @param loadOnce
     * @returns {Promise<any[]>}
     */
    loadScripts(tag, scripts, loadOnce?: boolean) {
        debugger;
        loadOnce = loadOnce || false;

        scripts.forEach((script: string) => {
            if (!this._scripts[script]) {
                this._scripts[script] = { src: script, loaded: false };
            }
        });

        const promises: any[] = [];
        scripts.forEach(script => promises.push(this.loadScript(tag, script, loadOnce)));

        return Promise.all(promises);
    }

    /**
     * Lazy load a single script
     * @param tag
     * @param {string} src
     * @param loadOnce
     * @returns {Promise<any>}
     */
    loadScript(tag, src: string, loadOnce?: boolean) {
        debugger;
        loadOnce = loadOnce || false;

        if (!this._scripts[src]) {
            this._scripts[src] = { src: src, loaded: false };
        }

        return new Promise((resolve, _reject) => {
            // resolve if already loaded
            if (this._scripts[src].loaded && loadOnce) {
                resolve({ src: src, loaded: true });
            } else {
                // load script tag
                const scriptTag = $('<script/>')
                    .attr('type', 'text/javascript')
                    .attr('src', this._scripts[src].src);

                $(tag).append(scriptTag);

                this._scripts[src] = { src: src, loaded: true };
                resolve({ src: src, loaded: true });
            }
        });
    }

    reloadOnSessionChange() {
        window.addEventListener('storage', function(data) {
            if (data['key'] === 'token' && data['oldValue'] == null && data['newValue']) {
                document.location.reload();
            }
        });
    }
}

0

sebuah sampel bisa

file script-loader.service.ts

import {Injectable} from '@angular/core';
import * as $ from 'jquery';

declare let document: any;

interface Script {
  src: string;
  loaded: boolean;
}

@Injectable()
export class ScriptLoaderService {
public _scripts: Script[] = [];

/**
* @deprecated
* @param tag
* @param {string} scripts
* @returns {Promise<any[]>}
*/
load(tag, ...scripts: string[]) {
scripts.forEach((src: string) => {
  if (!this._scripts[src]) {
    this._scripts[src] = {src: src, loaded: false};
  }
});

let promises: any[] = [];
scripts.forEach((src) => promises.push(this.loadScript(tag, src)));

return Promise.all(promises);
}

 /**
 * Lazy load list of scripts
 * @param tag
 * @param scripts
 * @param loadOnce
 * @returns {Promise<any[]>}
 */
loadScripts(tag, scripts, loadOnce?: boolean) {
loadOnce = loadOnce || false;

scripts.forEach((script: string) => {
  if (!this._scripts[script]) {
    this._scripts[script] = {src: script, loaded: false};
  }
});

let promises: any[] = [];
scripts.forEach(
    (script) => promises.push(this.loadScript(tag, script, loadOnce)));

return Promise.all(promises);
}

/**
 * Lazy load a single script
 * @param tag
 * @param {string} src
 * @param loadOnce
 * @returns {Promise<any>}
 */
loadScript(tag, src: string, loadOnce?: boolean) {
loadOnce = loadOnce || false;

if (!this._scripts[src]) {
  this._scripts[src] = {src: src, loaded: false};
}

return new Promise((resolve, reject) => {
  // resolve if already loaded
  if (this._scripts[src].loaded && loadOnce) {
    resolve({src: src, loaded: true});
  }
  else {
    // load script tag
    let scriptTag = $('<script/>').
        attr('type', 'text/javascript').
        attr('src', this._scripts[src].src);

    $(tag).append(scriptTag);

    this._scripts[src] = {src: src, loaded: true};
    resolve({src: src, loaded: true});
  }
 });
 }
 }

dan penggunaan

suntikan pertama

  constructor(
  private _script: ScriptLoaderService) {
  }

kemudian

ngAfterViewInit()  {
this._script.loadScripts('app-wizard-wizard-3',
['assets/demo/default/custom/crud/wizard/wizard.js']);

}

atau

    this._script.loadScripts('body', [
  'assets/vendors/base/vendors.bundle.js',
  'assets/demo/default/base/scripts.bundle.js'], true).then(() => {
  Helpers.setLoading(false);
  this.handleFormSwitch();
  this.handleSignInFormSubmit();
  this.handleSignUpFormSubmit();
  this.handleForgetPasswordFormSubmit();
});
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.