Как загрузить библиотеки до вызова am4core в amCharts? - PullRequest
0 голосов
/ 01 августа 2020

У меня есть требование, по которому я должен создать собственный виджет с помощью Amcharts. Но я столкнулся с проблемой, что перед загрузкой библиотек вызывается функция am4core.

HTML Код

<com-sap-sample-helloworld5></com-sap-sample-helloworld5>

Js код


(function () {
  const amchartscorejs = "https://www.amcharts.com/lib/4/core.js";
  const amchartschartsjs = "https://www.amcharts.com/lib/4/charts.js";
  const amchartsanimatedjs = "https://www.amcharts.com/lib/4/themes/animated.js";
  const vennchartjs = "https://cdn.amcharts.com/lib/4/plugins/venn.js";
  async function LoadLibs() {
    console.log("LoadLibs");
    try {
      await loadScript(amchartscorejs);
      await loadScript(amchartschartsjs);
      await loadScript(amchartsanimatedjs);
      await loadScript(vennchartjs);
    } catch (e) {
      alert(e);
    } finally {
      that._firstConnection = 1;
    }
  }
  LoadLibs();
  function loadScript(src) {
    console.log("LoadScript");
    return new Promise(function (resolve, reject) {
      let script = document.createElement("script");
      script.src = src;
      script.onload = () => {
        console.log("Load: " + src);
        resolve(script);
      };
      script.onerror = () => reject(new Error(`Script load error for ${src}`));
      document.head.appendChild(script);
    });
  }
  let template = document.createElement("template");
  template.innerHTML = `<div id="chartdiv" width="100%" height="500px"></div>`;
  customElements.define(
    "com-sap-sample-helloworld5",
    class HelloWorld extends HTMLElement {
      constructor() {
        super();
        let shadowRoot = this.attachShadow({
          mode: "open",
        });
        shadowRoot.appendChild(template.content.cloneNode(true));
        this._firstConnection = false;
        this.addEventListener("click", (event) => {
          var event = new Event("onClick");
          this.dispatchEvent(event);
        });
      }
      //Fired when the widget is added to the html DOM of the page
      connectedCallback() {
        this._firstConnection = true;
        this.redraw();
      }
      //Fired when the widget is removed from the html DOM of the page (e.g. by hide)
      disconnectedCallback() {}
      //When the custom widget is updated, the Custom Widget SDK framework executes this function first
      onCustomWidgetBeforeUpdate(oChangedProperties) {}
      //When the custom widget is updated, the Custom Widget SDK framework executes this function after the update
      onCustomWidgetAfterUpdate(oChangedProperties) {
        if (this._firstConnection) {
          this.redraw();
        }
      }
      //When the custom widget is removed from the canvas or the analytic application is closed
      onCustomWidgetDestroy() {}
      //When the custom widget is resized on the canvas, the Custom Widget SDK framework executes the following JavaScript function call on the custom widget
      // Commented out by default
      /*
    onCustomWidgetResize(width, height){
    
    }
    */
      get chartType() {
        return this.chartTypeValue;
      }
      set chartType(value) {
        this.chartTypeValue = value;
      }

      redraw() {
        console.log("redraw function");
        // Themes begin
        am4core.useTheme(am4themes_animated);
        // Themes end
        var data = [
          { name: "A", value: 10 },
          {
            name: "B",
            value: 10,
          },
          {
            name: "C",
            value: 10,
          },
          {
            name: "X",
            value: 2,
            sets: ["A", "B"],
          },
          {
            name: "Y",
            value: 2,
            sets: ["A", "C"],
          },
          {
            name: "Z",
            value: 2,
            sets: ["B", "C"],
          },
          {
            name: "Q",
            value: 1,
            sets: ["A", "B", "C"],
          },
        ];

        var chart = am4core.create("chartdiv", am4plugins_venn.VennDiagram);
        var series = chart.series.push(new am4plugins_venn.VennSeries());
        series.dataFields.category = "name";
        series.dataFields.value = "value";
        series.dataFields.intersections = "sets";
        series.data = data;
        chart.legend = new am4charts.Legend();
        chart.legend.marginTop = 40;
      }
    }
  );
})();


Пожалуйста, сообщите какие изменения мне следует сделать, чтобы сначала загружались библиотеки amCharts, а затем вызывалась функция redraw ().

Вы также можете проверить журналы в jsfiddle , чтобы понять, с какой проблемой я столкнулся.

Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 02 августа 2020

Я начал с Promise.all , но закончил с проблемой Amcharts , которую нужно загружать последовательно.

ES7 awaits выполнит эту работу

Вы были на полпути, но остановили асинхронную c часть и продолжили синхронизацию c loadLibs, похоже, вы тогда изо всех сил пытались пометить элемент при загрузке был готово.

Здесь библиотеки загружаются последовательно с помощью функции generi c loadScripts , готов к использованию нескольких пользовательских элементов в зависимости от AmCharts, скрипт загружается только один раз:

(function() {
  function log() {
    let args = [...arguments];
    //console.log(`%c ${args.shift()} `, "background:lightgreen", ...args); //Chrome!
    document.body.append(args.join` `,document.createElement('BR'));
  }
  log("start IIFE script");
  async function loadScripts() {
    const load = (src) => new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = `https://www.amcharts.com/lib/4/${src}.js`;
      if (document.querySelector(`[src="${script.src}"]`)) resolve();
      log("load", script.src)
      script.onload = resolve;
      //script.onerror = () => reject(new Error(`Script load error for ${src}`));
      document.head.append(script)
    });
    await load("core"); // must be loaded first
    await load("charts");
    await load("themes/animated");
    await load("plugins/venn");
    return "return not even required";
  }
  customElements.define('my-element', class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode: "open"}).innerHTML = `<div>Executing:</div>`;
      log('connectedCallback');
      loadScripts().then(result => {
        log('done loading', result);
      });
    }
  });

})();
<my-element></my-element>

Вы можете переместить все внутрь (теперь async отмечен) connectedCallback

  async connectedCallback() {
    const load = (src) => new Promise((resolve, reject) => {
      let script = document.createElement('script');
      script.src = `https://www.amcharts.com/lib/4/${src}.js`;
      //if (document.querySelector(`[src="${script.src}"]`)) resolve();
      script.onload = resolve;
      this.append(script); // fine, doesn't really matter where SCRIPT is injected
    });
    await load("core");
    await load("charts");
    await load("themes/animated");
    await load("plugins/venn");
    // Loaded all
  }

Если connectedCallback выполняется несколько раз, потому что вы перемещаете узлы DOM, вам нужна эта проверка для уже загруженного скрипта

Код выше в JSFiddle: https://jsfiddle.net/CustomElementsExamples/wz79gbum/

0 голосов
/ 02 августа 2020

Переместите часть кода, в которой вы запускаете am4core, в отдельный файл и загрузите его, как другие, но измените также свою loadScript функцию, чтобы использовать defer:

 function loadScript(src)
{
console.log("LoadScript");
  return new Promise(function(resolve, reject) 
{
    let script = document.createElement('script');
    script.src = src;
    script.defer = true;

    script.onload = () => {console.log("Load: " + src); resolve(script);}
    script.onerror = () => reject(new Error(`Script load error for ${src}`));

    document.head.appendChild(script)
  });
}

Кстати, я думаю всегда лучше иметь теги вместе, поэтому замените последнюю строку:

document.head.appendChild(script)

на:

var scriptTag = document.head.getElementsByTagName('script')[0];
scriptTag.parentNode.insertBefore(script, scriptTag.nextSibling);
...