Data providers
Charts read data through a small data provider interface, so datasets can stay in whatever shape the host application already has. Two shapes are supported out of the box:
import { ArrayOfObjectsDataProvider, ObjectOfArraysDataProvider } from '@mochart/core';
// one object per category
new ArrayOfObjectsDataProvider(
[{ month: 'Jan', revenue: 10 }, { month: 'Feb', revenue: 20 }],
'month' // the category property
);
// one array per property
new ObjectOfArraysDataProvider(
{ month: ['Jan', 'Feb'], revenue: [10, 20] },
'month'
);createDefaultChart wraps its data array in an ArrayOfObjectsDataProvider automatically, using categoryAxis.property as the category property. The lower-level createChart accepts any object implementing the DataProvider interface, so a custom provider can read straight from an existing store without copying — see when the data changes for how to tell the chart the store moved.
Both built-ins guard the most common wiring mistake: a category property that matches nothing — absent from every row, or a missing or all-undefined category column — is reported through the provider's getError(), and the chart renders that message as its error state instead of silently indexing every row under the same key.
When the data changes
The chart pulls values through the provider when it (re)computes its chart data — not on every frame. Recomputation is triggered by prop identity: update only sees a config, data, or dataProvider change when a new object reference is passed. Mutating the store a custom provider reads from — or mutating a data array in place — changes what the provider would return, but nothing tells the chart to ask again.
Two ways to tell it:
- Pass a new identity. A new
dataarray (default charts) or a new provider instance (createChart) — the natural fit for immutable stores. The change animates as a normal data update. - Call
refresh(). Re-reads the current inputs without a new reference: a default chart rebuilds its provider over thedataarray, and acreateChartchart first calls the provider's optionalrefresh()hook and then re-reads it — the escape hatch made for live, store-backed providers. The built-in providers implement the hook by re-indexing their dataset, sorefreshpicks up any in-place change, including added, removed, and replaced rows. A custom provider that caches anything off its store should implementrefresh()to invalidate that cache; a provider that reads straight through needs nothing.
Both paths animate to the new values. See Updating and destroying for the full ChartHandle semantics.
Which properties are read
The config decides which properties the chart pulls from the provider:
- the category value from
categoryAxis.property(and optionallydisplayPropertyfor friendlier labels) - each series' value from its
property, plus the optionalrangeProperty,markerProperty,colorProperty,labelProperty,tooltipProperty,errorLowProperty, anderrorHighProperty.
Series values must be numeric or undefined — how missing values render is controlled per series with missingValues. Pair it with missingValueMarkers to keep a marker at the missing values — most useful with missingValues: 'base', which gives the marker a position.
Validating data against a config
getDataErrors checks a dataset against an enhanced config — non-numeric series values, category values that don't match the configured type, duplicate category values — and returns readable messages. A provider that exposes its category property (getCategoryProperty — both built-ins do) also gets its keying checked against categoryAxis.property. On a linear category scale, out-of-order category values are flagged too when a line or area series would zigzag through them; monotonic data in either direction passes, order-independent charts (bars, scatter) are not checked, and displayProperty configs are exempt since their display values may legitimately fold back across a DST-style repeated hour. Note that a series property absent from every row is not an error: it reads as all-undefined, which is valid missing-value data. A category property that matches nothing is caught earlier — the built-in providers report it through getError(), and getDataErrors defers to a provider-reported error rather than repeating it.
import { enhanceConfig, getDataErrors, ArrayOfObjectsDataProvider } from '@mochart/core';
const errors = getDataErrors(enhanceConfig(config), new ArrayOfObjectsDataProvider(data, 'month'));
// e.g. ["series values must be numeric or undefined for property: revenue"]Who runs this check depends on the entry point. Default charts (createDefaultChart, the bindings' DefaultChart) validate for you: they re-run getDataErrors whenever the config or data changes and show the error state when it fails. Managed charts (createChart, the bindings' Chart) trust the enhanced config and provider they are given — validation is the host's job there, so run getDataErrors whenever your config or data changes if the inputs aren't guaranteed valid.
This is the same check the docs run over every example on this site in CI.