Asynchronous data sources can be used to lazy load data on demand.
An asynchronous data source represents one or more tables of data.
import { Component } from "@angular/core";
import { AgStudio } from "ag-studio-angular";
import {
AgDataEngine,
AgDataSourcesDefinition,
AgFieldDefinition,
AgReportState,
AgStudioApi,
AgStudioApiReadyEvent,
AgStudioMode,
AgStudioProperties,
} from "ag-studio";
@Component({
selector: "my-app",
standalone: true,
imports: [AgStudio],
template: `<div style="display: flex; flex-direction: column; height: 100%">
<ag-studio
style="width: 100%; height: 100%;"
class="my-studio-container"
[initialState]="initialState"
[mode]="mode"
[data]="data"
/>
</div> `,
})
export class AppComponent {
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,
},
},
};
mode: AgStudioMode = "edit";
data: AgDataSourcesDefinition | AgDataEngine = {
sources: [
{
id: "medalsSource",
dataShape: "row",
getData: async () => {
const response = await fetch(
"https://www.ag-grid.com/studio/example-assets/olympic-winners.json",
);
const data = await response.json();
return { data };
},
tables: [
{
id: "medals",
fields,
},
],
},
],
};
}
const fields: AgFieldDefinition[] = [
{
id: "athlete",
format: "textFormat",
},
{
id: "age",
format: "integerFormat",
},
{
id: "country",
format: "textFormat",
},
{
id: "year",
format: "integerFormat",
formatOptions: { format: "0" },
},
{
id: "date",
format: "dateFormat",
},
{
id: "sport",
format: "textFormat",
},
{
id: "gold",
format: "integerFormat",
},
{
id: "silver",
format: "integerFormat",
},
{
id: "bronze",
format: "integerFormat",
},
{
id: "total",
format: "integerFormat",
},
];
importScripts('https://cdn.jsdelivr.net/npm/typescript@5.4.5/lib/typescript.min.js');
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', (event) => event.waitUntil(self.clients.claim()));
async function transpile(request, ext) {
const response = await fetch(request);
if (!response.ok) return response;
const source = await response.text();
const result = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ESNext,
jsx: ext.endsWith('x') ? ts.JsxEmit.React : undefined,
experimentalDecorators: ext === 'ts',
emitDecoratorMetadata: ext === 'ts',
},
});
return new Response(result.outputText, {
headers: { 'Content-Type': 'application/javascript' },
});
}
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
const ext = url.pathname
.match(/\.([a-z0-9]+)$/i)
?.at(1)
?.toLowerCase();
if (['jsx', 'ts', 'tsx'].includes(ext)) {
event.respondWith(transpile(event.request, ext));
}
});
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()],
});
<ag-studio
[data]="data"
/* other studio properties ... */ />
this.data = {
sources: [{
id: 'medalsSource',
dataShape: 'row',
getData: async (tableId) => {
const data = await fetchData(tableId);
return { data };
},
tables: [
{
id: 'medals',
fields: [
{
id: 'athlete',
format: 'textFormat',
},
// ... other fields
],
},
// ... other tables
],
}],
};Asynchronous data sources can return row-based or column-based data. This is determined by the dataShape property.
Properties available on the AgDataSourceDefinition<TDataShape extends AgDataShape, TRegistry extends AgBaseRegistry = AgDefaultRegistry> interface.
Data source ID
|
Data source display name. If not provided, a formatted version of id will be used.
|
Callback to return the data for the provided table and fields
|
'row' if the data is row-based, or 'column' if the data is column-based.
|
One or more tables that are provided by this data source.
|
Multiple Tables Copy Link
When multiple tables are provided, they can be linked by providing Relationships.
Reloading Data Copy Link
Data can be reloaded by calling api.reload().
import { Component } from "@angular/core";
import { AgStudio } from "ag-studio-angular";
import {
AgDataEngine,
AgDataSourcesDefinition,
AgFieldDefinition,
AgReportState,
AgStudioApi,
AgStudioApiReadyEvent,
AgStudioMode,
AgStudioProperties,
} from "ag-studio";
@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 id="reload" (click)="reload()">Reload</button>
</div>
</div>
<ag-studio
style="width: 100%; height: 100%;"
class="my-studio-container"
[initialState]="initialState"
[mode]="mode"
[data]="data"
(apiReady)="onApiReady($event)"
/>
</div> `,
})
export class AppComponent {
private studioApi!: AgStudioApi;
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" },
],
},
},
"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,
},
},
};
mode: AgStudioMode = "edit";
data: AgDataSourcesDefinition | AgDataEngine = {
sources: [
{
id: "medalsSource",
dataShape: "row",
getData: async () => ({ data: await dataPromise }),
tables: [
{
id: "medals",
fields,
},
],
},
],
};
reload() {
dataPromise = loadData();
this.studioApi.reload();
}
onApiReady(params: AgStudioApiReadyEvent) {
this.studioApi = params.api;
}
}
const fields: AgFieldDefinition[] = [
{
id: "athlete",
format: "textFormat",
},
{
id: "country",
format: "textFormat",
},
{
id: "year",
format: "integerFormat",
formatOptions: { format: "0" },
},
{
id: "sport",
format: "textFormat",
},
{
id: "gold",
format: "integerFormat",
},
{
id: "silver",
format: "integerFormat",
},
{
id: "bronze",
format: "integerFormat",
},
];
async function loadData(): Promise<Record<string, any>[]> {
const response = await fetch(
"https://www.ag-grid.com/studio/example-assets/olympic-winners.json",
);
const sourceData = await response.json();
return sourceData
.slice(
Math.floor(window.agRandom() * 100),
200 + Math.floor(window.agRandom() * 100),
)
.map((row: any) => ({
...row,
gold: Math.floor(window.agRandom() * 3),
silver: Math.floor(window.agRandom() * 4),
bronze: Math.floor(window.agRandom() * 4),
}));
}
let dataPromise: Promise<Record<string, any>[]> = loadData();
importScripts('https://cdn.jsdelivr.net/npm/typescript@5.4.5/lib/typescript.min.js');
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', (event) => event.waitUntil(self.clients.claim()));
async function transpile(request, ext) {
const response = await fetch(request);
if (!response.ok) return response;
const source = await response.text();
const result = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ESNext,
jsx: ext.endsWith('x') ? ts.JsxEmit.React : undefined,
experimentalDecorators: ext === 'ts',
emitDecoratorMetadata: ext === 'ts',
},
});
return new Response(result.outputText, {
headers: { 'Content-Type': 'application/javascript' },
});
}
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
const ext = url.pathname
.match(/\.([a-z0-9]+)$/i)
?.at(1)
?.toLowerCase();
if (['jsx', 'ts', 'tsx'].includes(ext)) {
event.respondWith(transpile(event.request, ext));
}
});
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()],
});