Create dynamic data visualizations using JavaScript with Apache ECharts. Charts run in both the browser and backend (Goja VM) with full RBAC enforcement.
Getting Started#
Creating a Chart#
- Navigate to any page or dashboard
- Click Add Widget → Scripted Chart
- Enter a name and description
- Write your JavaScript code
- Save and preview
Chart Structure#
Every chart script follows this pattern:
function generateChart(api, params, context) {
// 1. Query data
const items = api.queryItems("type = 'task'");
// 2. Transform data
const grouped = api.groupBy(items, 'status');
// 3. Return chart configuration
return echarts.createPieChart(grouped, {
title: 'Task Status Distribution',
colors: ['#4ade80', '#fbbf24', '#f87171']
});
}
API Reference#
Global Variables#
| Variable | Type | Description |
|---|---|---|
userId | string | Current user ID |
groupId | string | Group ID (if group-scoped) |
instanceId | string | Instance ID |
scriptId | string | Current script ID |
scriptType | string | "chart" |
itemId | string | Item ID (if item-scoped) |
api Object#
| Method | Returns | Description |
|---|---|---|
api.queryItems(query) | Item[] | Query items with RBAC |
api.groupBy(items, field) | object | Group items by field value |
api.aggregate(items, field, fn) | number | Sum, avg, min, max, count |
api.filterItems(items, predicate) | Item[] | Filter using predicate function |
echarts Object#
| Method | Returns | Description |
|---|---|---|
echarts.createPieChart(data, options?) | object | Create pie chart config |
echarts.createBarChart(data, options?) | object | Create bar chart config |
echarts.createLineChart(data, options?) | object | Create line chart config |
echarts.createScatterChart(data, options?) | object | Create scatter chart config |
Item Object#
| Property | Type | Description |
|---|---|---|
item.ID() | string | Item identifier |
item.Title() | string | Item title |
item.Status() | string | Current status ID |
item.AssignedTo() | string | Assignee user ID |
item.DueDate() | string | Due date (ISO 8601) |
item.IsCompleted() | boolean | Whether completed |
Examples#
Status Pie Chart#
function generateChart(api, params, context) {
const items = api.queryItems("type = 'task'");
const byStatus = api.groupBy(items, 'status');
return echarts.createPieChart(byStatus, {
title: 'Task Status Distribution',
colors: ['#4ade80', '#fbbf24', '#f87171', '#60a5fa']
});
}
See full example: examples/scripted-charts/01_status_pie_chart.js
User Workload Bar Chart#
function generateChart(api, params, context) {
const items = api.queryItems("type = 'task'");
const byUser = api.groupBy(items, 'assignee');
const labels = Object.keys(byUser);
const values = labels.map(u => byUser[u].length);
return echarts.createBarChart({
labels: labels,
values: values
}, {
title: 'User Workload',
xLabel: 'User',
yLabel: 'Tasks'
});
}
See full example: examples/scripted-charts/02_user_workload_bar.js
Priority Scatter Plot#
function generateChart(api, params, context) {
const items = api.queryItems("type = 'task'");
const data = items
.filter(i => i.DueDate() !== null)
.map(i => ({
value: [
new Date(i.DueDate()).getTime(),
getPriorityScore(i.GetField('priority'))
],
item: i
}));
return echarts.createScatterChart(data, {
title: 'Priority vs Due Date',
xLabel: 'Due Date',
yLabel: 'Priority'
});
}
function getPriorityScore(priority) {
switch(priority) {
case 'critical': return 4;
case 'high': return 3;
case 'medium': return 2;
case 'low': return 1;
default: return 0;
}
}
See full example: examples/scripted-charts/03_priority_scatter.js
Query Syntax#
Use the Timill query syntax to filter items:
// Query open tasks
api.queryItems("type = 'task' status = 'open'")
// Query with date range
api.queryItems("type = 'task' dueDate >= 2024-01-01")
// Query by assignee
api.queryItems("type = 'task' assignee = '" + userId + "'")
// Complex query
api.queryItems("type = 'task' (status = 'open' OR status = 'in-progress') priority = 'high'")
Best Practices#
Performance#
- Keep query filters specific to reduce data transfer
- Use
api.groupBy()instead of manual iteration - Cache expensive computations
Error Handling#
function generateChart(api, params, context) {
try {
const items = api.queryItems("type = 'task'");
if (!items || items.length === 0) {
return { error: 'No data available' };
}
// ... generate chart
} catch (err) {
return { error: err.message };
}
}
Security#
- All queries are automatically RBAC-enforced
- Scripts cannot access the file system
- External API calls are rate-limited
- Scripts timeout after 10 seconds
Troubleshooting#
Chart Not Rendering#
- Check browser console for JavaScript errors
- Verify the query returns data (test in backend scripts first)
- Ensure ECharts methods return the correct format
Query Returns No Data#
- Verify item type names are correct
- Check RBAC permissions for current user
- Test with a simpler query
Timeout Errors#
Scripts timeout after 10 seconds. If your chart is timing out:
- Simplify the query
- Reduce data transformations
- Check for infinite loops
Next Steps#
- See Scripting Overview for all scripting environments
- View Examples Gallery for copy-paste examples
- Read API Reference for complete API documentation