components icon indicating copy to clipboard operation
components copied to clipboard

Issue reRendering the TonicChart component

Open sahuguet opened this issue 2 years ago • 2 comments

I am using the TonicChart component. It renders well.

I am trying to make a change programmatically, e.g. changing the width via document.getElementsByTagName('tonic-chart')[0].reRender({'width': '400px', 'height': '600px', 'src': '{"labels":["Foo","Bar","Bazz"],"datasets":[{"label":"Quxx (millions)","backgroundColor":["#c3c3c3","#f06653","#8f8f8f"],"data":[278,467,34]}]}'})

The component does not render anymore.

Here is a self-contained HTML page that contains the TonicChart code (mostly unchanged). I have just hardcoded the chart.js and forced the data to be read from the src attribute.

<html>

<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<script type="module">
import Tonic from './@socketsupply/tonic/index.js'

export class TonicChart extends Tonic {
  static stylesheet () {
    return `
      tonic-chart {
        display: inline-block;
        position: relative;
      }

      tonic-chart canvas {
        display: inline-block;
        position: relative;
      }
    `
  }


  draw (data = {}, options = {}) {
    const root = this.querySelector('canvas')
    const type = this.props.type || options.type

    if (!this.Chart) {
      console.error('No chart constructor found')
      return
    }

    return new this.Chart(root, {
      type,
      options,
      data
    })
  }

  async redraw () {
    return this.connected()
  }

  async fetch (url, opts = {}) {
    if (!url) return {}

    try {
      const res = await window.fetch(url, opts)
      return { data: await res.json() }
    } catch (err) {
      return { err }
    }
  }

  async connected () {
    let data = null
    this.Chart = Chart // hard-coded


    if (!this.Chart) return

    const options = {
      ...this.props,
      ...this.props.options
    }

    const src = this.props.src
    console.log(src)

    /*
    if (typeof src === 'string') {
      const response = await this.fetch(src)

      if (response.err) {
        console.error(response.err)
        data = {}
      } else {
        data = response.data
      }
    }

    if (src === Object(src)) {
      console.log('object')
      data = src
    }
    */
   data = JSON.parse(src)
    console.log(data)

    if (data && data.chartData) {
      throw new Error('chartData propery deprecated')
    }

    if (data) {
      this.draw(data, options)
    }
  }

  render () {
    const {
      width,
      height
    } = this.props

    this.style.width = width
    this.style.height = height

    return this.html`
      <canvas ...${{ width, height }}>
      </canvas>
    `
  }
}

Tonic.add(TonicChart, 'tonic-chart');
</script>
</head>


<body>
This is a test.

<tonic-chart
    width="500px"
    height="250px"
    src='{"labels":["Foo","Bar","Bazz"],"datasets":[{"label":"Quxx (millions)","backgroundColor":["#c3c3c3","#f06653","#8f8f8f"],"data":[278,467,34]}]}'
    type="line">
</tonic-chart>



</body>

</html>

sahuguet avatar Feb 07 '23 03:02 sahuguet

This issue I see is that the reRender method calls the render method which only returns an empty canvas and does not update the underlying chart.

How could I make the render method returns the canvas AND trigger a chart update?

sahuguet avatar Feb 07 '23 03:02 sahuguet

I managed to get something working using the updated method. Is it the best way to do it?

sahuguet avatar Feb 07 '23 15:02 sahuguet