Angular2 с Typescript, как использовать файл Plnload CDN script в ленивом загруженном модуле?

Я хотел бы использовать Plupload в компоненте Angular2 и получить доступ к файлу JavaScript Plupload из CDN. Я хочу, чтобы он был специфичным для компонента, чтобы он не был загружен, если он не требуется - я хочу, чтобы он был в ленивом загруженном модуле. Как я могу это сделать?

Теперь полностью ответили на эту страницу!

В результате этого квеста, который включал предложение и награждение щедроты для двух людей, которые упорно работали со мной на этом, заключается в следующем:

  • Пример использования Plupload с Angular 2 и TypeScript
  • Как ленить загрузить script из CDN в Angular 2
  • Пример использования Plupload в ленивом загруженном модуле
  • Как использовать ленивый загруженный script в Angular 2

    (см. историю изменений для уродливых деталей, которые использовались для составления этого вопроса.)

Ответ 1

Вот обзор того, что вам нужно сделать, чтобы создать ленивую загрузку функции Plupload при загрузке Plupload из CDN:

  • Когда функция нужна (например, пользователь нажимает кнопку или посещает страницу), динамически добавляйте тег <script> на страницу, чтобы загрузить библиотеку Plupload из CDN.
  • Подождите, пока библиотека не будет загружена для продолжения (или вы можете получить ошибку "plupload is undefined").
  • Отобразить пользовательский интерфейс для взаимодействия с Plupload в одном из ваших шаблонов Angular. В своей простейшей форме этот пользовательский интерфейс состоит из двух кнопок: "Выбрать файлы" и "Загрузить файлы".
  • Инициализировать Plupload и подключить его к пользовательскому интерфейсу.

Завершен, рабочий код: https://plnkr.co/edit/4t39Rod4YNAOrHmZdxqc?p=preview

В моей реализации обратите внимание на следующие моменты:

  • Относительно # 2. Лучший способ проверить, закончилась ли загрузка Plupload, - опросить глобальное пространство имен для существования переменной plupload. Пока window.plupload не существует, это значит, что библиотека еще не загружена и что мы НЕ должны действовать. Для простоты мой код просто ждет одну секунду и продолжается.
  • Число 4 может показаться немного сложным. Plupload активно использует прямой доступ DOM для подключения своего API к HTML (например, document.getElementById('filelist')). Это что-то Angular обескураживает и вам следует стараться избегать, когда это возможно. Более конкретно, прямой доступ DOM используется в следующих местах:
    • Чтобы сообщить Plupload, какой элемент DOM должен запускать диалог "Выбрать файлы" (то, что они называют опцией browse_button config). Для этого я не мог избежать прямой ссылки DOM, и я использовал декоратор @ViewChild, чтобы удержать кнопку "Выбрать файлы" .
    • Отображение выбранных файлов в шаблоне. Для этого я преобразовал синтаксис Plupload в обычный синтаксис Angular. Я подталкиваю выбранные файлы к свойству класса fileList, которое я показываю в шаблоне, используя стандартный *ngFor.
    • Кнопка "Загрузить файлы" запускает код, который выполняет фактическую загрузку и обновляет пользовательский интерфейс, чтобы показать ход загрузки. Еще раз, я преобразовал это в обычный синтаксис Angular, используя привязку события и привязку данных.

Сообщите мне, если у вас есть какие-либо вопросы.

Ответ 2

В этом подходе нет необходимости в дополнительных модулях загрузчика.

См. пример (check console for Woohoo): http://plnkr.co/edit/gfUs4Uhe8kMGzPxwpBay?p=preview обновленный плункер: https://plnkr.co/edit/leG062tg7uX8sLrA0i2i?p=preview

Вы можете lazyload некоторые js, добавив к вам документ script:

Создайте my-lazy-load.function.ts:

export function lazyload(url) {
    // based on https://friendlybit.com/js/lazy-loading-asyncronous-javascript/

    let scripts = document.getElementsByTagName('script');
    for (let i = scripts.length; i--;) {
        if (scripts[i].src.match(url)) return true;
    }

    let s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.src = url;
    let x = document.getElementsByTagName('script')[0];
    x.parentNode.insertBefore(s, x);

    return true;
}

В вашем компоненте, который вы хотите добавить plupload:

import {lazyload} from "./my-lazy-load.function.ts";

export class MyComponent  implements OnInit {

    pluploadInterval:number = null;
    hasPlupload: boolean = false;

    ngOnInit() {
        lazyload("https://cdnjs.cloudflare.com/ajax/libs/plupload/2.3.1/plupload.full.min.js");

        this.pluploadInterval = window.setInterval(()=>{
            if(window.plupload) { // you can check existence of plupload object any way you want

                // Woohoo, I am ready to be used

                this.hasPlupload = true; // everything is run outside ngZone, wrap it if angular2 is not reacting to changes, or change window.setInterval to setInterval
                window.clearInterval(this.pluploadInterval); // don't forget to clean interval
            }
        }, 100); // timeinterval can vary 
....

Браузер автоматически загрузит его.

Обратите внимание, что if(plupload) он предполагает, что добавлен глобальный объект plupload, добавленный script (я не знаю, действительно ли он был добавлен, проверьте свой рабочий пример в чистом javascript). Поскольку это расширение jquery, вы можете проверить его прототипом следующим образом: jQuery тест для того, есть ли у объекта метод?

СТАРЫЙ ИСТОРИЧЕСКИЙ: @Reid здесь plunker: https://plnkr.co/edit/zDWWQbTQUSHBqCsrUMUi?p=preview plupload фактически загружен, но добавлен для запроса с помощью define("plupload", ['./moxie'], extract); Я не уверен в данный момент как извлечь оттуда и какой пакет требуется, принадлежит... код для поиска правильного загрузчика модуля принадлежит самой plupload, вот он (из plupload.dev.js):

if (typeof define === "function" && define.amd) {
    define("plupload", ['./moxie'], extract);
} else if (typeof module === "object" && module.exports) {
    module.exports = extract(require('./moxie'));
} else {
    global.plupload = extract(global.moxie);
}

Ответ 3

Я считаю, что лучше всего использовать библиотеку Require.js, чтобы вы могли динамически загружать свои скрипты из своих компонентов.

Небольшой компромисс заключается в том, что вам придется добавить эту библиотеку 18 КБ на страницу index.html(CDN), однако это может сэкономить вам огромное количество загрузок, если ваши сторонние библиотеки массивны.

У меня нет опыта использования plupload, поэтому вместо этого я собрал следующий plunkr, который использует внешнюю библиотеку анимации, взятую из CDN. Plunkr оживляет число от 0 до 100.

https://plnkr.co/edit/fJCtezsERYHOYplLh7Jo?p=preview


index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.3/require.min.js"></script>

component.ts

ngOnInit(){
    // Dynamically loads the framework from the CDN.
    require(["https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenLite.min.js"],
    // This function is then called ONCE LOAD IS COMPLETE
    function (common) {
      // Do greensock things
      var demo = {score:0},
      scoreDisplay = document.getElementById("scoreDisplay");

      //create a tween that changes the value of the score property of the demo object from 0 to 100 over the course of 20 seconds.
      var tween = TweenLite.to(demo, 20, {score:100, onUpdate:showScore})

      //each time the tween updates this function will be called.
      function showScore() {
        scoreDisplay.innerHTML = demo.score.toFixed(2);
      }  
    });
  }

Что мне нравится в этом подходе, так это то, что в обратном вызове onLoad из require синтаксис не изменяется от обычной реализации библиотеки, поэтому вы можете просто скопировать вставку вашего текущего рабочего кода в обратный вызов.