如何在 amCharts 中调用 am4core 之前加载库?

How to load libraries before am4core is called in amCharts?

我有一个要求,我必须使用 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 中的日志以了解我遇到的问题。

提前致谢。

将您触发 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);

我从, but ended up on an Amcharts Issue开始说Amcharts需要按顺序加载

ES7 等待将完成这项工作

您已经进行到一半,但停止了异步部分并继续同步 loadLibs,看起来您在加载完成后努力尝试 标记 元素.

此处库按顺序加载,使用通用 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/