Chart Configuration Manager
This example demonstrates a dynamic chart system with configuration management using Chart.js and the BuzzyFrameAPI for data persistence.
Key Features
Interactive Chart.js integration with dynamic data visualization
Configuration persistence storing chart settings as child data
Real-time chart updates when configurations change
Multiple chart configurations with version management
Chart destruction and recreation for smooth transitions
Sample data generation for demonstration purposes
Use Cases
Perfect for applications requiring:
Business intelligence dashboards
Data analytics platforms
Reporting systems with customizable charts
Configuration management interfaces
A/B testing for chart presentations
Historical chart configuration tracking
Implementation Overview
This widget uses the Child Table Data pattern to store chart configurations as JSON objects. Key features include:
Chart.js library integration for professional visualizations
Configuration versioning and management
Real-time chart updates without page refresh
Clean chart lifecycle management (destroy/recreate)
Sample data for immediate demonstration
Code Example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Chart Configuration Manager</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js"
crossorigin
></script>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js"
crossorigin
></script>
<script
src="https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js"
crossorigin
></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const buzzyFrameAPI = new BuzzyFrameAPI();
let appInitData = null;
let appConfigs = [];
async function initApp() {
appInitData = await buzzyFrameAPI.initialise();
await fetchAndUpdateConfigs();
buzzyFrameAPI.addMicroappListener({
microAppID: appInitData.rowJSON._id,
listener: async () => {
console.log('Config data updated, refreshing');
await fetchAndUpdateConfigs();
},
});
}
async function fetchAndUpdateConfigs() {
try {
const { rowJSON = {} } = appInitData || {};
const children = await buzzyFrameAPI.getChildItemsByField({
appID: rowJSON._id,
fieldID: rowJSON.fieldID,
});
appConfigs = children;
if (window.updateConfigs) {
window.updateConfigs(appConfigs);
}
} catch (error) {
console.error('Error fetching configs:', error);
if (window.updateConfigs) {
window.updateConfigs([]);
}
}
}
function App() {
const { useEffect, useState, useRef } = React;
const [configs, setConfigs] = useState([]);
const [loading, setLoading] = useState(true);
const chartRef = useRef(null);
const canvasRef = useRef(null);
useEffect(() => {
window.updateConfigs = newConfigs => {
setConfigs(newConfigs);
setLoading(false);
};
if (appConfigs.length > 0) {
setConfigs(appConfigs);
setLoading(false);
}
return () => {
window.updateConfigs = null;
};
}, []);
useEffect(() => {
if (configs.length > 0 && canvasRef.current) {
const latestConfig = configs[configs.length - 1].content;
if (chartRef.current) {
chartRef.current.destroy();
}
const ctx = canvasRef.current.getContext('2d');
chartRef.current = new Chart(ctx, {
type: latestConfig.type || 'bar',
data: latestConfig.data || {},
options: latestConfig.options || {},
});
}
}, [configs]);
const handleSaveConfig = async () => {
try {
const { rowJSON } = appInitData;
const newConfig = {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [
{
label: 'Sample Data',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)',
],
borderWidth: 1,
},
],
},
options: {
scales: {
y: {
beginAtZero: true,
},
},
},
};
await buzzyFrameAPI.createMicroappChild({
microAppResourceID: rowJSON.parentResourceID,
appID: rowJSON._id,
fieldID: rowJSON.fieldID,
content: newConfig,
});
} catch (error) {
console.error('Error saving config:', error);
}
};
const handleDeleteConfig = async childID => {
if (!confirm('Are you sure you want to delete this configuration?'))
return;
try {
await buzzyFrameAPI.removeMicroappChild({ childID });
} catch (error) {
console.error('Error deleting config:', error);
}
};
if (loading)
return <div className="has-text-centered p-4">Loading...</div>;
return (
<div className="container">
<div className="box">
<canvas ref={canvasRef}></canvas>
</div>
<div className="box">
<h3 className="title is-4 mb-4">Chart Configurations</h3>
<button
className="button is-primary mb-4"
onClick={handleSaveConfig}>
<span className="icon">
<i className="fas fa-save"></i>
</span>
<span>Save Current Config</span>
</button>
<div className="content">
{configs.map((config, index) => (
<div key={config._id} className="level is-mobile">
<div className="level-left">
<div className="level-item">
<span className="has-text-weight-medium">
Config {index + 1}
</span>
</div>
</div>
<div className="level-right">
<div className="level-item">
<button
className="button is-danger is-small"
onClick={() => handleDeleteConfig(config._id)}>
<span className="icon is-small">
<i className="fas fa-trash"></i>
</span>
<span>Delete</span>
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}
initApp().then(() => {
ReactDOM.render(<App />, document.getElementById('root'));
});
</script>
</body>
</html>
Key Concepts
Chart.js Integration
External Library: Demonstrates how to integrate popular JavaScript libraries
Chart Lifecycle: Proper chart destruction and recreation for smooth updates
Canvas Management: Direct canvas manipulation with React refs
Configuration Objects: Complete Chart.js configuration stored as JSON
Configuration Management
Persistence: Chart configurations stored as child data for long-term access
Versioning: Multiple configurations can be saved and managed
Real-time Updates: Charts update immediately when configurations change
JSON Storage: Complex configuration objects stored efficiently
Data Visualization Patterns
Sample Data: Includes sample datasets for immediate demonstration
Color Schemes: Professional color palettes for visual appeal
Responsive Design: Charts adapt to container size automatically
Interactive Elements: Hover effects and tooltips built into Chart.js
Advanced Features
Memory Management: Proper cleanup of chart instances to prevent memory leaks
Error Handling: Graceful handling of configuration errors
User Feedback: Loading states and confirmation dialogs
Extensibility: Easy to extend with additional chart types and options
This example showcases how code widgets can integrate sophisticated charting libraries while maintaining data persistence and real-time updates, making it perfect for business intelligence and analytics applications.
Last updated