demo D238
Two metrics, two scales
A total and an average on independent left and right axes, each labelled so neither misleads
measures: [{ col, fn }, { col, fn, axis: 'right' }]
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.25.0/lattice-grid.min.css">
<style>
#chart { height: 360px; }
#grid { margin-top: 14px; }
#grid > div { height: 340px; }
</style>
<div id="app"></div>
<script type="module">
import * as Vue from 'https://esm.sh/vue@3';
import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.25.0/lattice-grid.esm.min.js';
import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.25.0/modules/vue.esm.min.js';
import { createChart } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.25.0/modules/charts.esm.min.js';
const LatticeGrid = createLatticeGrid({ vue: Vue, createGrid });
Vue.createApp({
components: { LatticeGrid },
data() {
return {
config: {
rowKey: 'id',
columns: [
{ field: 'circuit', title: 'Circuit', layout: { width: 180 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'status', title: 'Status', filter: { type: 'set' } },
{ field: 'bandwidth', title: 'Bandwidth', type: 'number' },
{ field: 'charge', title: 'Monthly charge', type: 'number',
format: { style: 'currency', currency: 'GBP' }, total: 'sum' },
],
rows,
},
};
},
mounted() {
// The component exposes the live grid via a template ref, which the chart draws from.
const grid = this.$refs.grid.grid();
// Two measures on independent scales: the second carries axis: 'right'.
createChart({
grid,
container: '#chart',
type: 'combo',
x: 'region',
measures: [
{ col: 'charge', fn: 'sum', type: 'bar', axis: 'left', label: 'Total charge' },
{ col: 'bandwidth', fn: 'mean', type: 'line', axis: 'right', label: 'Mean bandwidth' },
],
});
},
template: `
<div id="chart"></div>
<div id="grid"><lattice-grid ref="grid" v-bind="config" /></div>
`,
}).mount('#app');
</script>
Reading two metrics on one chart when their scales are nothing alike
Some comparisons only make sense side by side even though the two numbers live on wildly different scales: total revenue against average order value, spend against a rate, volume against a percentage. Plot them on one shared axis and the smaller of the two becomes a flat line along the floor, telling the reader nothing. A dual-axis chart in any JavaScript charting layer gives each metric its own axis, so both keep a scale that suits them. In Lattice Grid a chart is built from the grid’s own filtered rows, and a measure carrying axis: 'right' is bound to an independent right-hand scale while the first measure keeps the left, so a large total drawn as bars and a small average drawn as a line each read clearly on the same picture.
Both axes are labelled by default, because an unlabelled second axis is how a dual-axis chart misleads: a reader cannot tell which mark belongs to which scale. Each axis takes the label from its measure, and the right axis is drawn in its series’ own colour so the eye can follow it. Like every chart here it reads the grid’s filtered rows, so filtering or sorting the grid redraws both series and rescales both axes on the next frame, with nothing to keep in step by hand.
How do you put two Y axes on one chart?
Give the chart a measures array instead of a single y, and mark the second measure with axis: 'right'. That measure is bound to its own scale on the right while the first stays on the left, so two metrics with very different ranges each stay readable. Set a label on each measure and both axes are titled, so the chart says which mark belongs to which scale.
When should a chart use a second axis instead of one?
Use a second axis when the two metrics genuinely differ in scale or unit, such as a running total against an average or a count against a percentage, so that plotting them on one axis would flatten the smaller into a line at zero. When both metrics share a scale, a single axis is clearer and a second axis only adds a scale the reader has to keep straight.