Builder, templates & tables
| Method | Returns | In short |
|---|---|---|
getTemplates() | ITemplateDefinition[] | The template library |
loadTemplates / setTemplates | void | Hand stored templates in |
saveTemplate / upsertTemplate | void | Add or change one |
deleteTemplate / removeTemplate | void | Remove one |
setTemplateActionMap / registerTemplateActionDataSource | void | Wire template actions |
loadSidebarGroups | void | Hand stored library items in |
saveSidebarGroup / upsertSidebarGroup | void | Add or change one |
deleteSidebarGroup / removeSidebarGroup | void | Remove one |
setTableSettings / getTableSettings | void / settings | Column layout per table |
setTableFilters / getTableFilters | void / filters | Active filters |
setTableSavedFilters / getTableSavedFilters | void / list | Saved filter sets |
request* methods | void | Ask the host for stored table state |
switchToTab / requestTabChange | void | Builder navigation |
isBuilderAttached, getBuilderAdapter, attachBuilderAdapter | Is a builder on screen? | |
setAiAssistantConfig / getAiAssistantConfig | void / config | AI assistant backend |
Templates
Templates are reusable HTML snippets creators manage in the Templates tab and use in tables, lists and cards (Templates). The library is yours to store: the builder tells you when a creator saves or deletes one ((templateSaved), (templateDeleted)), and you hand the stored ones back on startup. A saved view only carries the templates its elements use.
// startup
api.loadTemplates(await firstValueFrom(http.get<ITemplateDefinition[]>('/api/templates')));
// persistence
api.onTemplateSaved.add(({ template, previousCode }) => http.put(`/api/templates/${template.code}`, template).subscribe());
api.onTemplateDeleted.add(({ code }) => http.delete(`/api/templates/${code}`).subscribe());A template is:
{
name: 'clientCard', // the code elements refer to; `code` is accepted as well
title: 'Client card',
content: '<div class="card">{{name}}</div>', // `html` is accepted as well
css: '.card { padding: 8px; }',
requestBodyJson: '',
extraJsonSettings: '',
}getTemplates()
getTemplates(): ITemplateDefinition[]The whole library: in the builder every template of the Templates tab, in a runtime every template of the view.
api.getTemplates().map((t) => t.name); // ['clientCard', 'orderRow']loadTemplates() / setTemplates()
loadTemplates(templates: INgxViewBuilderTemplateInput[], replace = true): void
setTemplates(templates: INgxViewBuilderTemplateInput[], replace = true): voidReplaces the library, or merges by name with replace = false. Fires onTemplatesLoaded; the Templates tab shows them at once.
saveTemplate() / upsertTemplate()
saveTemplate(template: INgxViewBuilderTemplateInput, previousCode?: string): void
upsertTemplate(template: INgxViewBuilderTemplateInput): voidAdds or replaces one template; previousCode renames. Other templates and the view stay as they are. Fires onTemplateSaved.
api.saveTemplate({ code: 'clientCard', content: '<b>{{name}}</b>' });
api.saveTemplate({ code: 'customerCard', content: '<b>{{name}}</b>' }, 'clientCard'); // renamedeleteTemplate() / removeTemplate()
deleteTemplate(code: string): void
removeTemplate(name: string): voidFires onTemplateDeleted.
setTemplateActionMap() / registerTemplateActionDataSource()
setTemplateActionMap(map: Record<string, string>): void
registerTemplateActionDataSource(functionName: string, dataSourceName: string): voidA template can call a function on click, (click)="loadClient(row.id)". These map such function names to data sources: the click runs the source with the arguments as its parameters. setTemplateActionMap replaces the whole map, registerTemplateActionDataSource adds one entry.
api.setTemplateActionMap({ loadClient: 'getClientById', archive: 'archiveClient' });
api.registerTemplateActionDataSource('loadOrders', 'getOrdersByClient');The element library
Creators save configured elements and whole sections into the builder's library (Copies, from an element's right click menu) and drag them into other views. Like templates, the library is yours to store.
api.loadSidebarGroups(await firstValueFrom(http.get('/api/builder-library')));
api.onSidebarGroupSaved.add(({ group }) => http.put(`/api/builder-library/${group.id}`, group).subscribe());
api.onSidebarGroupDeleted.add(({ code }) => http.delete(`/api/builder-library/${code}`).subscribe());An item carries the element and, for containers, the layout below it:
{
id: 'b7c1e0f2', // `code` is accepted as well
label: 'Address block',
type: 'panel',
icon: 'dashboard', // optional, the type's icon by default
element: { name: 'address', type: 'panel', label: 'Address' },
segmentRows: [...], // rows inside the element
segmentElements: { street: {...}, city: {...} },
createdAt: '2026-10-08T09:30:00.000Z',
}loadSidebarGroups()
loadSidebarGroups(groups: INgxViewBuilderSidebarGroupInput[], replace = true): void
setSidebarGroups(groups, replace = true): void
setSidebarTemplates(templates, replace = true): voidReplaces the library, or adds to it with replace = false. The three names do the same. Fires onSidebarGroupsLoaded.
saveSidebarGroup() / upsertSidebarGroup()
saveSidebarGroup(group: INgxViewBuilderSidebarGroupInput, previousCode?: string): void
upsertSidebarGroup(group: INgxViewBuilderSidebarGroupInput): voidAdds or replaces one item. Only element is required; a missing id, label or type is filled in. Fires onSidebarGroupSaved.
api.saveSidebarGroup({
id: 'vatField',
label: 'VAT code',
element: { name: 'vatCode', type: 'text', label: 'VAT code', placeholder: 'LT100000000000' },
});deleteSidebarGroup() / removeSidebarGroup()
deleteSidebarGroup(code: string): void
removeSidebarGroup(code: string): voidFires onSidebarGroupDeleted.
Tables
Tables let people hide and reorder columns, filter and save filter sets. Store those per user and table and hand them back, so the table opens the way the person left it. Identify a table by its element name.
setTableSettings() / getTableSettings()
setTableSettings(identifier: string, settings: INgxViewBuilderTableColumnSetting[], options?: { tableName?: string; elementName?: string; source?: 'host' | 'localStorage' | 'dataSource' | 'user' }): void
getTableSettings(identifier: string): INgxViewBuilderTableColumnSetting[] | nullapi.setTableSettings('clients', [
{ key: 'name', visible: true, order: 0, width: '240px' },
{ key: 'email', visible: true, order: 1 },
{ key: 'createdAt', visible: false, order: 2 },
], { source: 'host' });The table applies it at once. Fires onTableSettingsChanged and onTableSettingsApplied; when the person changes columns, onTableSettingsChanged comes with source: 'user'.
setTableFilters() / getTableFilters()
setTableFilters(identifier: string, filters: unknown[], options?: { tableName?: string; elementName?: string; source?: 'host' | 'user' | 'savedFilter'; savedFilter?: INgxViewBuilderTableSavedFilter | null }): void
getTableFilters(identifier: string): unknown[] | nullApplies detailed search filters. Each filter names a column, a condition and a value:
api.setTableFilters('clients', [
{ key: 'status', operator: '=', value: 'active', type: 'text' },
{ key: 'name', operator: '%-%', value: 'har', type: 'text' },
{ key: 'createdAt', operator: '>=', value: '2026-01-01', type: 'date' },
]);operator | Meaning |
|---|---|
%-% / !%-% | contains / does not contain |
%- / -% | starts with / ends with |
= / != | equals / not equals |
> >= < <= | number and date comparisons |
Fires onTableFiltersChanged and onTableFiltersApplied.
setTableSavedFilters() / getTableSavedFilters()
setTableSavedFilters(identifier: string, filters: INgxViewBuilderTableSavedFilter[], options?): void
getTableSavedFilters(identifier: string): INgxViewBuilderTableSavedFilter[] | nullapi.setTableSavedFilters('clients', [
{ id: 'f1', name: 'Active in Vilnius', filters: [
{ key: 'status', operator: '=', value: 'active', type: 'text' },
{ key: 'city', operator: '=', value: 'Vilnius', type: 'text' },
] },
]);Asking the host for table state
requestTableSettings(tableName: string, elementName: string, currentSettings: INgxViewBuilderTableColumnSetting[]): void
requestTableSettingsSave(tableName: string, elementName: string, settings: INgxViewBuilderTableColumnSetting[]): void
requestTableSavedFilters(tableName: string, elementName: string, currentFilters: INgxViewBuilderTableSavedFilter[]): void
requestTableSavedFilterSave(tableName: string, elementName: string, filter: INgxViewBuilderTableSavedFilter, currentFilters: INgxViewBuilderTableSavedFilter[]): voidA table whose settings or saved filters live with the host calls these; each fires the matching onTable...Requested event. Answer with the setters above:
api.onTableSettingsRequested.add(async ({ tableName }) => {
const stored = await prefs.get(`table:${tableName}`);
if (stored) api.setTableSettings(tableName, stored, { source: 'host' });
});
api.onTableSettingsSaveRequested.add(({ tableName, settings }) => prefs.set(`table:${tableName}`, settings));The builder
switchToTab() / requestTabChange()
switchToTab(tab: 'editor' | 'preview' | 'jsonEditor' | 'formSettings' | 'translations' | 'variables' | string): void
requestTabChange(tab: string): voidswitchToTab changes the builder's tab; a plugin tab is addressed by its code ('templates'). requestTabChange only fires onTabChangeRequested, for hosts that wrap the navigation themselves. Without a builder on screen both do nothing visible.
api.switchToTab('preview');isBuilderAttached() / getBuilderAdapter() / attachBuilderAdapter()
isBuilderAttached(): boolean
getBuilderAdapter(): INgxViewBuilderBuilderAdapter | null
attachBuilderAdapter(adapter: INgxViewBuilderBuilderAdapter | null): voidisBuilderAttached() tells shared code whether it runs next to the builder or a plain runtime. The adapter is how the builder connects itself; hosts do not attach one.
if (api.isBuilderAttached()) {
// designer page: the root owns the edited view
}setAiAssistantConfig() / getAiAssistantConfig()
setAiAssistantConfig(config: { backendUrl?: string | null } | null): void
getAiAssistantConfig(): { backendUrl?: string | null }Fires onAiAssistantConfigChanged. For connecting an AI client to the builder, see AI command API.