hybridsjs/hybrids

Issue rendering Apexchart

miguelrk opened this issue · 2 comments

Hello! First of all thanks for this wonderful library! After using it for basic components I'm really loving the 'hybrid' approach. I have been trying to create a wrapper for apexcharts and I am having some issues, maybe somebody can help. I believe it has to do with the lifecycleless approach of hybrids, which I haven't quite yet wrapped my head around yet, maybe I'm doing something wrong to render the chart. Here is my code:

import { html } from "hybrids";
import ApexCharts from "apexcharts";

const chartOptions = {
  chart: { type: "bar" },
  series: [ { name: "sales", data: [30, 40, 45, 50, 49, 60, 70, 91, 125] } ],
  xaxis: { categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999] }
};

function renderChart(host) {
  const el = document.createElement("div");
  let chart = new ApexCharts(el, chartOptions);
  chart.render();
  return el;
}

export default {
  render: (host) => html`${renderChart(host)}`
};

Edit <simple-counter> web component built with hybrids library (forked)

And I get the following error in the browser console:

apexcharts.common.js?ba30:14 Uncaught (in promise) TypeError: Cannot read property 'resizeTriggers' of null
at window.addResizeListener (apexcharts.common.js?ba30:14)
at eval (apexcharts.common.js?ba30:14)
at new Promise ()
at t.value (apexcharts.common.js?ba30:14)
at renderChart (index.js?88db:22)
at render (index.js?88db:28)
at get (render.js?d2d8:15)
at get (cache.js?be57:79)
at eval (cache.js?be57:180)
at eval (emitter.js?9b74:8)

Maybe this issue has to do with it...

A more fundamental question is maybe if I this is the proper way of mounting charts/components in a div container. Or what would be a better approach? Thanks! any help is greatly appreciated!

Hi @miguelrk. In the linked issue I can see, that the library must be initialized on the DOM element connected to the document. In your code example, you are creating a div and apply the library before passing it to the template. Additionally, It looks that the library does not support Shadow DOM. You can avoid that problems for example in the following way:

function apexCharts(options) {
  return {
    get: (host) => {
      const chart = new ApexCharts(host, options);
      chart.render();
      
      return chart;
    },
    observe() {}, // otherwise the property won't be called, as it is not a dep of `content`
  }
}

const chartOptions = {
  chart: { type: "bar" },
  series: [ { name: "sales", data: [30, 40, 45, 50, 49, 60, 70, 91, 125] } ],
  xaxis: { categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999] }
};

export default {
  chart: apexCharts(chartOptions),
  render: () => html`<slot></slot>`.css`
    ::slotted(*) {
       ...;
    }
  `,
};

In the above code, there is a reusable apexCharts property factory, which uses a host element as a target. You can still style them using shadow dom, by the slot element. The limitation is that one chart for one element, but you can create a structure in light DOM, and then query those elements. The render factory can target light DOM (you can use content property name with a function, or use render() factory explicitly).

Hey @smalluban !! Thanks for the detailed response!! It worked like a charm! Happy to close this and I hope this helps other developers