Using ZingChart with Svelte 3

ZingChart is a rich JavaScript data visualization library with support for over 50 chart types and modules. In this article we'll see how to get started with ZingChart and Svelte and look at tapping into some ZingChart features from within a Svelte app. This is not meant to be an exhaustive review of ZingChart's capabilities, to learn more about what ZingChart can do visit the ZingChart home page. The GitHub repository corresponding with this article is here and the ZingChart-Svelte site documentation can be found here.

ZingChart is written in vanilla JavaScript and can be used in any web application, with or without a framework. We've written a wrapper to provide easy access to ZingChart from Svelte. We'll start out with Svelte template and quickly get a static chart displayed. Then we'll look at each of the pages in the demo and highlight how to integrate ZingChart features like modules, events, and invoking methods on charts within a Svelte context. We won't build the entire demo app in this article, so I recommend that you download the demo and start it up as a reference.

Hello World

First, though, let's create a Hello World ZingChart/Svelte app. We'll start by creating a new Svelte app, installing the zingchart-svelte adapter, and starting the app. Note that installing the adapter also installs ZingChart itself.

npx degit sveltejs/template zingchart-svelte-helloworld
cd zingchart-svelte-helloworld
npm install
npm install -D @zingsoft/zingchart-svelte
npm run dev

You can now view the app in a browser at localhost:8080 (check the output of npm run dev, it might be running on a different port).

The full demo contains 7 pages demonstrating different aspects of using ZingChart in Svelte. For this Hello World example, we'll just add a static chart to the App.svelte page.

Let's start by adding the import for the ZingChart/Svelte adapter, which will also import ZingChart. We'll also remove the name component property:

zingchart-svelte-helloworld/src/App.svelte
<script>
  import ZingChart from '@zingsoft/zingchart-svelte'
</script>

Now remove the contents of <main> and replace them with a ZingChart component:

zingchart-svelte-helloworld/src/App.svelte
<main>
  <ZingChart />
</main>

In the browser you should now see a mostly empty page with the Powered by ZingChart logo. The chart is empty because we haven't given it a configuration yet. In the <script> section, define a config object and then add it as the data attribute of the ZingChart component. Here's the whole file except for the <style> section:

zingchart-svelte-helloworld/src/App.svelte
<script>
  import ZingChart from '@zingsoft/zingchart-svelte'

  const config = {
      type: 'bar',
      series: [{
        values: [4,5,3,4,5,3,5,4,11]
      }]
    }
</script>

<main>
  <ZingChart data={config} />
</main>

You should now see a bar chart in the browser!

The config object contains two properties. The first is type, which selects which chart to display -- in this case, a bar chart. The second property is series, which is an array of objects that contain the values and configuration for each dataset in the chart. In this case we're displaying one dataset and specifying its values as an array of numbers. With no further configuration, the barchart axes are scaled to include the full range of the data and the bars are colored blue and have tooltips showing the numerical values.

This app can be used to play around with all of the different chart types and configuration parameters described in the ZingChart docs. Let's move on from here to look at various aspects of using ZingChart features in a Svelte app.

zingchart-svelte-demo

For this part of the article you should have the demo running locally. I'll highlight key sections of the source code with a focus on the interaction between Svelte and ZingChart.

In an environment with nodejs, npm, and git installed:

git clone https://github.com/zingchart-demos/zingchart-svelte-demo.git
cd zingchart-svelte-demo
npm install
npm run dev

You should now be able to view the demo in your browser at localhost:5000. Each of the demo pages is contained in a file under zingchart-svelte-demo/src/demos/.

Hello World (zingchart-svelte-demo/src/demos/Simple.svelte)

The Hello World demo is identical to the simple example we created above in zingchart-svelte-helloworld/src/App.svelte. The only differences are that the ZingChart component is not wrapped in a <main> element and the <style> section is not needed.

US Map (zingchart-svelte-demo/src/demos/ModuleChart.svelte)

The primary purpose of this demo is to demonstrate how modules can be included for use in a ZingChart. ZingChart keeps file size to a minimum by implementing some functionality in optional modules.

In this case we're doing mapping, so we need the maps module and a module containing the specific map data we want to view. We're using the ES6 (ECMAScript 6) version of ZingChart, so we import the corresponding modules:

zingchart-svelte-demo/src/demos/ModuleChart.svelte
import ZingChart from '@zingsoft/zingchart-svelte'
import 'zingchart/modules-es6/zingchart-maps.min.svelte'
import 'zingchart/modules-es6/zingchart-maps-usa.min.svelte'

Here's the config object for this chart:

zingchart-svelte-demo/src/demos/ModuleChart.svelte
const config = {
  shapes: [
    {
      type: 'zingchart.maps',
      options: {
        name: 'usa',
        ignore: ['AK', 'HI']
      }
    }
  ]
}

This configuration selects a chart of shapes, which is an array of objects. Here we have one shape object of type zingchart.maps consisting of the USA map minus Alaska and Hawaii. This is all we need to display a zoomable map of the lower 48 US states with tooltips.

Interaction (zingchart-svelte-demo/src/demos/ModuleDrag.svelte)

This page demonstrates using the ZingChart dragging module to interact with the data on a barchart. We also need to obtain a Svelte reference to the chart component so we can invoke ZingChart methods.

First we import the dragging module.

zingchart-svelte-demo/src/demos/ModuleDrag.svelte
import 'zingchart/modules-es6/zingchart-dragging.min.js'

Inside the component function, we create a Svelte reference called chart and assign it to the ZingChart component when we create it, using bind:this:

zingchart-svelte-demo/src/demos/ModuleDrag.svelte
let chart
...
<ZingChart bind:this={chart} data={config} height='600px' modules='dragging' zingchart_plugins_dragging_complete={showData} />

Note that, when we create the ZingChart component, we also enable the dragging module. That module provides an attribute we can use to specify the function to call when the user has completed a dragging interaction with one of the bars in the chart: showData. Here's that function's definition:

zingchart-svelte-demo/src/demos/ModuleDrag.svelte
function showData() {
  const data = chart.exec('getseriesdata')

  goodDays = countGoodDays(data[0].values, data[0].goals)
}

When we obtain a Svelte reference to a ZingChart component, all of the methods provided by the ZingChart API are available, accessible via the exec function. The first argument is the name of the method, any additional arguments can be specified afterwards. There's no need to specify the ID for the chart: chart.exec('methodName'...) is identical to zingchart.exec('chartID', 'methodName' ...). In this case, we're invoking chart.exec('getseriesdata') to obtain the series data from the chart. Let's look at the configuration for this chart:

zingchart-svelte-demo/src/demos/ModuleDrag.svelte
const config = {   
  type: 'vbullet',
  title: {
    text: 'Pushups Per Day'
  },
  subtitle: {
    text: 'Bars are draggable'
  },
  plot: {
    valueBox: [
      {
        type: 'all',
        text: '[%node-value / %node-goal-value]',
        color: '#000',
        placement: 'goal'
      }
    ]
  },
  scaleX: {
    labels: ['Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun']
  },
  tooltip: {
    borderRadius: '3px',
    borderWidth: '0px',
    fontSize: '14px',
    shadow: true
  },
  series: [
    {
      values: startingValues,
      dataDragging: true,
      goal: {
        backgroundColor: '#64b5f6',
        borderWidth: '0px'
      },
      goals: goals,
      rules: [
        {
          backgroundColor: '#81c784',
          rule: '%v >= %g'
        },
        {
          backgroundColor: '#ef5350',
          rule: '%v < %g/2'
        },
        {
          backgroundColor: '#ffca28',
          rule: '%v >= %g/2 && %v < %g'
        }
      ]
    }
  ]
})

There's a lot going on here, but note that, like in the Hello World barchart, there is a series property that's an array of objects, consisting of one object with a values property containing an array of numbers. This object also enables dragging on just this dataset with the dataDragging property. When we invoke chart.exec('getseriesdata') the current state of the series property is returned and assigned to data. data[0].values contains the values corresponding to where the user has dragged each of the bars on the chart, and we use that to set the goodDays variable, which is displayed above the barchart.

The configuration for this chart demonstrates how multiple elements can be shown in the same chart. In this case, the plot property adds the non-draggable goal markers and text accompanying each bar in the chart. The configuration also sets the title, subtitle, tooltip options, and the labels for the X axis. The series object not only sets the values for the bars, it also defines the goals array that positions the goal markers. In addition it defines rules that declaratively specify the color to use for each bar in terms of the relationship between the bar value %v and the bar goal %g.

Reconfiguring (zingchart-svelte-demo/src/demos/Dynamic.svelte)

Up to now we've stored the ZingChart configuration using a variable local to the Svelte component, but we haven't modified it. This demo shows a dynamic line plot using Svelte's onMount function to start an interval timer to update the chart's state.

We construct the configuration for a line plot with some random data values and make it modifiable by using let instead of const:

zingchart-svelte-demo/src/demos/Dynamic.svelte
let config = {
  type: 'line',
  series: [
    {values: randomData(nValues)}
  ]
}

When we create the ZingChart component we just use config as we have before, as the data property. We also display config.series[0].values in a div above the chart. Both the chart and the div will be re-rendered each time we change config.

zingchart-svelte-demo/src/demos/Dynamic.svelte
<div>
  <div>{JSON.stringify(config.series[0].values)}</div>
  <ZingChart data={config} />
</div>

We use Svelte's onMount function to start the interval timer that invokes shuffle() once a second, after the component has been mounted. onDestroy is used to clear the interval timer when this component is unmounted.

zingchart-svelte-demo/src/demos/Dynamic.svelte
let interval

onMount(() => interval = setInterval(shuffle, period))
onDestroy(() => clearInterval(interval))

The shuffle function modifies the config.series array to contain an object with a new, random values array:

zingchart-svelte-demo/src/demos/Dynamic.svelte
function shuffle() {
  config.series = [{values: randomData(nValues)}]
}

Events (zingchart-svelte-demo/src/demos/Events.svelte)

This demo shows how to listen for events occuring on a ZingChart. We're going draw a line plot and watch for an event indicating the chart has finished rendering and for mouse-over events on the nodes of the line plot.

We start by declaring the configuration for a simple line plot and two Svelte component variables:

zingchart-svelte-demo/src/demos/Events.svelte
const config = {
  type: 'line',
  series: [{
    values: [4,5,3,4,5,3,5,4,11]
  }]
}

let output = ''
let renderState = 'pending'

The HTML defined in this component looks like this:

zingchart-svelte-demo/src/demos/Events.svelte
<div class="wrapper">
  <ZingChart data={config} complete={chartDone} node_mouseover={nodeInfo} />
  <div class="output">
    <h2>Output from events</h2>
    <div class="bound">
      Events bound:
      <ul>
        {#each listOfEventListeners as ev}
          <li>{ev}</li>
        {/each}
      </ul>
    </div>
    <div class="renderState">Chart is {renderState}</div>
    <pre class="nodeInfo">{output}</pre>
  </div>
</div>

The ZingChart component specifies the functions to call when the complete and node_mouseover events occur. The rest of the component displays the corresponding renderState and output variables.

The event functions themselves are straight-forward. chartDone just sets renderState to rendered:

zingchart-svelte-demo/src/demos/Events.svelte
function chartDone() {
  renderState = 'rendered'
}

Since there's no perceptible delay in rendering the chart, we never get to see pending being displayed.

nodeInfo receives an argument containing information about the mouse-over event. The ev property of this object contains the JavaScript event data, which includes some circular references that make it impossible to render as JSON (plus it's not interesting), so we remove info.ev before rendering info as JSON and storing it in output:

zingchart-svelte-demo/src/demos/Events.svelte
function nodeInfo(info) {
  delete info.ev     // Remove the event data
  output = `Node Info\n${JSON.stringify(info,null,2)}\n`
}

Some of the properties of info pertain to the chart itself and don't change, but as you mouse over different nodes you can see changing values like the index and value of the node, and its (x,y) coordinates in the rendered chart image.

Methods (zingchart-svelte-demo/src/demos/Methods.svelte)

This demo is another example of using a Svelte reference to the ZingChart component in order to invoke methods on it. In this case we're using the addplot method to add an additional random dataset to a bar chart:

zingchart-svelte-demo/src/demos/Methods.svelte
function addDataset() {
  chart.exec('addplot', {
    data: {
      values: randomData(nValues)
    }
  })

  nSets++
}

Multiple Plots (zingchart-svelte-demo/src/demos/License.svelte)

The purpose of this final demo is to show how to access the zingchart object in order to access properties and methods that operate on ZingChart globally (see ZingChart Object and Methods).

Importing @zingsoft/zingchart-svelte creates the zingchart global variable, so we don't need to modify the import:

zingchart-svelte-demo/src/demos/License.svelte

We can then use zingchart to set some performance flags:

import ZingChart from '@zingsoft/zingchart-svelte'
zingchart-svelte-demo/src/demos/License.svelte
zingchart.DEV.KEEPSOURCE = 0 // prevents lib from storing the original data package
zingchart.DEV.COPYDATA = 0 // prevents lib from creating a copy of the data package 

ZingChart provides a branded license that allows free use of the library as long as the ZingChart watermark is visible (see ZingChart Pricing). In situations where you do need a license, you can set the license key using the zingchart global:

zingchart-svelte-demo/src/demos/License.svelte
zingchart.LICENSE = ['abcdefghijklmnopqrstuvwxy']

This demo also shows how to display multiple plots in one ZingChart instance. The configuration object used here:

zingchart-svelte-demo/src/demos/License.svelte
const config = {
  graphset: [
    { 
      type: 'line',
      height: '200px',
      width: '90%',
      x: '5%',
      y: '5%',
      series:[
        {
          values:[76,23,15,85,13]
        },
        {
          values:[36,53,65,25,45]
        }
      ]
    },
    { 
      type: 'funnel',
      height: '55%',
      width: '45%',
      x: '5%',
      y: '200px',
      series:[
        {values:[30]},
        {values:[15]},
        {values:[5]},
        {values:[3]}
      ]
    },
    {
      type: 'pie',
      height: '55%',
      width: '45%',
      x: '50%',
      y: '200px',
      series:[
        {values:[15]},
        {values:[30]},
        {values:[34]}
      ]
    }
  ]
}

consists of a graphset array containing three objects, each of which is a ZingChart configuration of the form that we've seen in each of the previous demos. In addition to specifying the type and data for each plot, the size and position of the plot is set using the height, width, x, and y properties. For more on creating sets of graphs in one ZingChart, see the graphset documentation.

Conclusion

We hope this article has provided a jumpstart in using ZingChart in a Svelte application. For more information, please visit the GitHub repository for the zingchart-svelte integration module https://github.com/zingchart/zingchart-svelte and the ZingChart-Svelte documentation at https://www.zingchart.com/docs/integrations/svelte.

Interested in learning more about ZingChart or trying it out for yourself? Let us know at support@zingchart.com or visit us at https://www.zingchart.com and chat directly with our team!