A Data Engine loads, processes, and caches data for Studio widgets.
Studio creates one automatically when you pass data sources via the data property, but you can create the built-in engine yourself to share it across instances, or cache it across a Studio instance's lifecycle.
Built-in Engine Copy Link
When you pass data sources directly to Studio, it creates a built-in Data Engine behind the scenes. Creating the engine externally with createDataEngine(data) gives you two benefits:
- Sharing - multiple Studio instances can point at the same engine, so they share a single copy of the data.
- Caching across lifecycles - the engine survives when Studio is destroyed and recreated, so data doesn't need to be re-fetched or reprocessed on remount.
import { HttpClient } from "@angular/common/http";
import type { OnInit } from "@angular/core";
import { Component, signal } from "@angular/core";
import type {
AgDataEngine,
AgDataSourcesDefinition,
AgReportState,
} from "ag-studio";
import { createDataEngine, enableStudioDevValidations } from "ag-studio";
import { AgStudio } from "ag-studio-angular";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableStudioDevValidations();
}
@Component({
selector: "my-app",
standalone: true,
imports: [AgStudio],
template: `<div style="display: flex; flex-direction: column; height: 100%">
<div class="example-controls">
<div class="controls-row">
<button (click)="recreate()">
{{
created() ? "Destroy Studio Instance" : "Recreate with Data Engine"
}}
</button>
</div>
</div>
@if (created()) {
<ag-studio
v-if="created"
style="width: 100%; height: 100%;"
class="my-studio-container"
[initialState]="initialState"
mode="edit"
[data]="data"
/>
}
@if (!created()) {
<div class="my-studio-container">No current Studio instance</div>
}
</div> `,
})
export class AppComponent implements OnInit {
initialState: AgReportState = {
pages: [
{
id: "page1",
widgets: {
"1": {
type: "grid",
dataMapping: {
cols: [
{ id: "medals.country" },
{ id: "medals.sport" },
{ id: "medals.gold", aggregation: "sum" },
{ id: "medals.silver", aggregation: "sum" },
{ id: "medals.bronze", aggregation: "sum" },
{ id: "medals.total", aggregation: "sum" },
],
},
},
"2": {
type: "column-chart-grouped",
dataMapping: {
categoryKey: [{ id: "medals.country" }],
valueKey: [
{ id: "medals.gold", aggregation: "sum" },
{ id: "medals.silver", aggregation: "sum" },
{ id: "medals.bronze", aggregation: "sum" },
],
tooltipKey: [],
},
},
},
widgetLayout: {
"1": {
xTrack: 0,
yTrack: 0,
xSpan: 24,
ySpan: 16,
},
"2": {
xTrack: 0,
yTrack: 16,
xSpan: 24,
ySpan: 16,
},
},
},
],
selectedPageId: "page1",
panels: {
filters: {
collapsed: true,
},
edit: {
collapsed: true,
},
},
};
created = signal(true);
data!: AgDataSourcesDefinition | AgDataEngine;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http
.get("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
.subscribe(
(data: any) =>
(this.data = createDataEngine({ sources: [{ id: "medals", data }] })),
);
}
recreate() {
this.created.update((currentCreated) => !currentCreated);
}
}
.my-studio-container {
color: var(--main-fg);
}
import '@angular/compiler';
import { provideHttpClient } from '@angular/common/http';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component.ts';
const app = bootstrapApplication(AppComponent, {
providers: [provideHttpClient()],
});
const dataEngine = createDataEngine({
sources: [{
id: 'medals',
data: [
{
year: 2000,
sport: 'Swimming',
country: 'United States',
// ... other fields
},
// ... other rows
],
}],
});<ag-studio
[data]="data"
/* other studio properties ... */ />
this.data = dataEngine;See Loading Data for the full range of data loading patterns.
createDataEngine(data) accepts a data object of type AgDataSourcesDefinition.
One or more data sources.
|
When using multiple related tables, this describes the fields that link the tables together.
|
Expression field definitions for calculated columns.
|
Overrides to existing formats, or additional custom formats.
|
AI-facing overview of the entire dataset: what it contains, what it's for, domain quirks.
|
Named time dimensions (calendars) that supply date fragments and a continuous date spine.
|
Additional date-fragment bucket definitions to register alongside the built-in set (year, quarter, month, week, day, monthOfYear, dayOfWeek, …). Use this to add project-specific groupings such as weekend, dayOfMonth, or hour that the built-in registry does not include. Provide via createBuckets so type-level registry inference works correctly.
|
Engine-wide behavioural options, such as fan-out detection policy.
|
Embedding Single Widgets Copy Link
A widget cannot be used on its own outside of Studio. To place an individual widget in your own application, run a Studio instance that shows a single widget filling the canvas, with the panels hidden. Several such instances can share one engine, so the data is loaded once.
import { HttpClient } from "@angular/common/http";
import type { OnInit } from "@angular/core";
import { Component } from "@angular/core";
import type { AgDataEngine, AgReportState, AgWidgetState } from "ag-studio";
import {
createDataEngine,
enableStudioDevValidations,
studioTheme,
} from "ag-studio";
import { AgStudio } from "ag-studio-angular";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableStudioDevValidations();
}
// A single full-canvas widget, no panels - a Studio instance acting as one embeddable widget.
function singleWidgetState(id: string, widget: AgWidgetState): AgReportState {
return {
pages: [
{
id: "page1",
widgets: { [id]: widget },
widgetLayout: { [id]: { xTrack: 0, yTrack: 0, xSpan: 1, ySpan: 1 } },
},
],
selectedPageId: "page1",
};
}
@Component({
selector: "my-app",
standalone: true,
imports: [AgStudio],
template: `<div class="single-widgets">
<ag-studio
class="single-widget"
mode="view"
[panels]="{}"
[layout]="layout"
[theme]="theme"
[initialState]="gridState"
[data]="data"
/>
<div class="app-area">Your Application</div>
<ag-studio
class="single-widget"
mode="view"
[panels]="{}"
[layout]="layout"
[theme]="theme"
[initialState]="chartState"
[data]="data"
/>
</div> `,
})
export class AppComponent implements OnInit {
// Remove the spacing around the canvas so the widget fills its instance edge to edge.
theme = studioTheme.withParams({ studioWrapperSpacing: 0 });
layout = {
minWidth: 300,
height: 300,
columns: 1,
rowHeight: 300,
pagePadding: 0,
widgetPadding: 0,
};
gridState: AgReportState = singleWidgetState("1", {
type: "grid",
dataMapping: {
cols: [
{ id: "medals.country" },
{ id: "medals.gold", aggregation: "sum" },
{ id: "medals.silver", aggregation: "sum" },
{ id: "medals.bronze", aggregation: "sum" },
],
},
});
chartState: AgReportState = singleWidgetState("2", {
type: "column-chart-grouped",
dataMapping: {
categoryKey: [{ id: "medals.country" }],
valueKey: [
{ id: "medals.gold", aggregation: "sum" },
{ id: "medals.silver", aggregation: "sum" },
{ id: "medals.bronze", aggregation: "sum" },
],
tooltipKey: [],
},
});
data!: AgDataEngine;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http
.get("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
// One engine, shared by both instances - the data is loaded and cached once.
.subscribe(
(data: any) =>
(this.data = createDataEngine({ sources: [{ id: "medals", data }] })),
);
}
}
.single-widgets {
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: center;
gap: 12px;
height: 300px;
color: var(--main-fg);
}
.single-widget {
flex: 1 1 0;
min-width: 0;
max-width: 360px;
height: 300px;
}
.app-area {
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 160px;
text-align: center;
border: 1px dashed var(--chart-border);
border-radius: 6px;
}
import '@angular/compiler';
import { provideHttpClient } from '@angular/common/http';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component.ts';
const app = bootstrapApplication(AppComponent, {
providers: [provideHttpClient()],
});
To show a single widget, give the report a one-cell layout and hide the panels:
<ag-studio
[mode]="mode"
[panels]="panels"
[layout]="layout"
/* other studio properties ... */ />
this.mode = 'view';
this.panels = {};
this.layout = { columns: 1, height: 300, rowHeight: 300, pagePadding: 0, widgetPadding: 0 };Each instance is independent. Panels belong to a single instance, so one panel cannot control several instances. Only the data engine is shared.
Custom Engines Copy Link
Sharing and caching, as described above, are features of the built-in engine only. For larger datasets, or when you want to replace query execution with a backend you already own, see the Custom Engine guide in the Server-Side Data section.