Convert "API Maker UI Maker" (JSON-config-driven grid + form) to hand-written Angular + PrimeNG
1. Title & Objective
You are an AI coding agent. Your task is to convert an API Maker "UI Maker" page β a single JSON configuration object (IDBMasterConfig, conventionally the variable dbMasterConfig) that drives a PrimeNG data grid plus add/edit/view reactive forms inside an iframe β into a normal, explicit, hand-written Angular + PrimeNG application: a p-table-based grid component and reactive FormGroup-based form components, where every feature currently expressed in JSON is implemented directly in Angular/PrimeNG TypeScript and templates.
Non-negotiable rule: ZERO FEATURE LOSS. Every form control, every property, every validation rule, every file-upload behavior, every dependent/cascading dropdown, every conditional show/hide, every dynamic-required rule, every nested field, every nested grid, every add-new-inline form, every API-call override, every CSV import/export behavior, every theme, every iframe/postMessage event, every row-level action-hiding flag, every custom action button, every grid grouping/sorting/pagination/selection/filter/search/column-selector behavior, and every operations-ordering rule must be reproduced exactly. Do not invent features that are not in the source config. Where the source config is ambiguous, inspect the original dbMasterConfig for that page and reproduce the observed runtime behavior.
Work page-by-page: for each UI Maker page you are given (identified by db-master-name), produce a dedicated Angular feature module/standalone component set. Read that page's dbMasterConfig in full first β it is the source of truth that overrides any general guidance below.
2. Background: What UI Maker is
UI Maker is an extension layered on top of API Maker. It takes one IDBMasterConfig object and renders, at runtime, a complete CRUD screen for a database table/collection:
- A PrimeNG data grid (
gridconfig) with columns, sorting, filtering, global search, pagination, grouping, row selection, row actions (edit/view/delete + custom), and a toolbar (add/import/export/refresh/column-selector/theme-selector/delete-all + custom actions). - Reactive add/edit/view forms (
form.fields, a 2-D array: outer = rows, inner = columns) built from 24 control types (EDBMasterFormControl). - Each field is bound to a dot-notation
path, supports static or JS-expression-stringvisible/disabled/requiredFun, can nest other fields (fields[][]) or a whole child grid (gridSettings). - Pages run inside an iframe and talk to the host via
window.postMessage. They call API Maker auto-generated POST endpoints for all data operations, each overridable viaapiCallOverrides. - Custom CSS (
cssCode[{runOn:'header', code}]) and JS lifecycle hooks (jsCode[{appendTo, code}]) can be injected globally (page) or per control. Code can be inline strings, functions, or references to external named functions (userUtils.Main.*per-page,userUtils.GeneralUtils.*shared). - Inbuilt libraries available to custom code: Papa Parse (
Papa), date-fns (dateFns), date-fns-tz (dateFnsTz); commonly added: lodash (_), moment. Inbuilt CSS: Bootstrap 5.3.3 grid + utilities.
UI Maker cannot run standalone β it is coupled to the API Maker backend that supplies data. Your conversion must therefore also implement an explicit Angular HttpClient service layer that calls the same backend endpoints.
3. Target architecture
Implement this structure (standalone Angular components, PrimeNG, ReactiveFormsModule):
src/app/
ui-maker-shared/ # shared, imported by EVERY converted page
services/
api.service.ts # HttpClient layer; replaces all API Maker calls + apiCallOverrides
query.service.ts # builds API Maker IQueryFormat (find/select/limit/deep/sort) bodies
parent-integration.service.ts # postMessage send/receive (iframe contract) OR @Input/@Output bridge
expression.service.ts # safe evaluator for visible/disabled/requiredFun JS-expression strings
hook-runner.service.ts # executes jsCode (string | function | userUtils.* ref) with $scope
message.service.ts # toast wrapper -> utils.messageService.*
file-upload.service.ts # upload/download/remove contract
theme.service.ts # theme switching
date-format.service.ts # date-fns <-> PrimeNG calendar format + tz conversion
utils/
general-utils.ts # <= userUtils.GeneralUtils.* (shared common JS)
form-path.util.ts # dot-notation path <-> nested FormGroup helpers
components/
um-grid/ # reusable generic p-table grid component (config-light, typed)
um-form/ # reusable form renderer OR base class for per-page forms
controls/ # one component per EDBMasterFormControl (24)
pages/
<db-master-name>/ # ONE per UI Maker page
<name>.component.ts/.html/.scss # page component: grid + form wiring, page jsCode hooks, page cssCode
<name>.main.ts # <= userUtils.Main.* (page-specific JS functions)
<name>.api.ts # page-specific endpoints / overrides
<name>.model.ts # typed row + form model interfaces
styles.scss # global: Bootstrap grid+utilities + shared cssCode (common CSS)
Recommended approach β per-page hand-written components backed by reusable building blocks. Build a small library of reusable, typed primitives (a generic <um-grid>, the 24 control components, the shared services), then hand-write one explicit component per UI Maker page that composes them with the page's exact behavior. Rationale: a fully generic runtime engine would just re-implement UI Maker; explicit per-page components are debuggable, type-safe, tree-shakeable, and let you delete dead config. Reuse the primitives so you do not re-implement PrimeNG wiring 24 times.
Key mappings:
- Reactive forms: each form is a
FormGroup. Dot-notationpath(a.b.c) maps to nestedFormGroups (form.get('a.b.c')). Provideform-path.util.tsto build the nested group from all fieldpaths and to get/set by path. - 2-D
fields[][]layout: outer array = a<div class="row">; inner array = the columns inside that row. Each field wrapper getscssClassDivclasses (defaultcol-lg mt-4 col-md-{calculated based on columns.length}β compute thecol-md-*span from the number of columns in that inner array). Bootstrap 5.3.3 grid is the layout system. - API layer replaces API Maker auto-generated calls and
apiCallOverrides(see Β§9). - Parent integration (
parent-integration.service.ts) reproduces the iframepostMessagecontract (see Β§12). If the converted app is no longer embedded as an iframe, replace inbound parent events with component@Input()s / route/query params and outbound events with@Output()EventEmitters β but preserve the same semantics and payloads.
4. Global vs Page-specific CSS & JS (READ CAREFULLY β explicit requirement)
UI Maker has two tiers of custom CSS and JS: common/shared (used across many or all pages) and page-specific. You must split them correctly.
4.1 CSS
| Source (UI Maker) | What it is | Target (Angular) |
|---|---|---|
| Common/global CSS used across many pages | shared style overrides | Put into the global styles.scss (or a shared _um-shared.scss imported there). Loaded once, applies everywhere. |
Page-specific CSS: config.cssCode: [{ runOn: 'header', code: '<raw CSS>' }] |
raw CSS injected into <head> for that page |
Put into that page component's stylesheet (<name>.scss). Note runOn is always 'header'. If a class must override already-applied global/theme classes (UI Maker allows overriding existing classes), keep the same selectors and use :host ::ng-deep or move it to global scope as needed to win specificity. |
| Inbuilt Bootstrap 5.3.3 grid + utilities | col-*, mt-*, row, etc. used by cssClassDiv |
Ensure Bootstrap grid + utilities CSS is loaded globally so cssClassDiv classes keep working. |
Mapping example:
// BEFORE (UI Maker page config)
cssCode: [{ runOn: 'header', code: `.font-orange { color: orange; }` }]
4.2 JS
UI Maker JS comes in three forms for the code value: an inline JS string, a function ($scope) => any, or a reference to a named external function (userUtils.Main.fnName or userUtils.GeneralUtils.fnName). Code may mutate the provided data in place, return a Promise (await it), or return a function (invoke it).
| Source (UI Maker) | What it is | Target (Angular) |
|---|---|---|
Common/global JS: userUtils.GeneralUtils.<fn> |
shared utilities callable from any page via code: 'userUtils.GeneralUtils.fn' |
Implement as methods/exports in ui-maker-shared/utils/general-utils.ts, imported by all pages. |
Page-specific JS (named): userUtils.Main.<fn> |
per-page functions (e.g. userUtils.Main.first_name_blur) referenced from config |
Implement as methods on that page's <name>.main.ts (or the page component). |
Page-level lifecycle hooks: top-level config.jsCode: [{ appendTo: EDBMasterConfigAppendTo, code }] |
run at named page lifecycle points | Implement as page component methods/lifecycle hooks (see table below). |
Per-control hooks: <controlSettings>.jsCode: [{ appendTo, code }] |
run at named control events | Implement as the control component's event handlers (see Β§7). |
Top-level page lifecycle hooks β EDBMasterConfigAppendTo β and their available variables. Translate each to the natural Angular equivalent:
appendTo |
Available variables in scope | Angular equivalent |
|---|---|---|
oncePageLoad |
(NONE β no config, no globalData) |
run once early in ngOnInit before context is ready |
oncePageLoadWithContext |
config, globalData, utils, queryParams |
run once in ngOnInit after config/parent context resolved |
gridRender |
gridEvent, gridData, globalData, utils, queryParams |
hook that runs every time grid data loads; may mutate gridData rows; may return a Promise (await before render) |
modifyGridRequest |
gridEvent, reqBody (IQueryFormat), globalData, utils, queryParams; also moment, _ available |
mutate the find/sort/select request body before the grid data fetch |
onceGridDataLoaded |
(grid data context) | run after grid data first loads |
preSaveAndEdit |
reqBody, globalData, utils, queryParams, mode ('save' \| 'edit' \| 'import') |
run before save/update/import; branch on mode; may delete fields from reqBody |
beforeSaveModalOpen |
formData, utils (form-introspection) |
mutate formData before the Add dialog opens (e.g. formData.remarkDate = new Date()) |
beforeEditModalOpen |
formData, utils |
mutate formData before the Edit dialog opens |
beforeViewModalOpen |
formData, utils |
mutate formData before the View dialog opens |
columnSelectionChanged |
selected columns | react to column-selector change |
deleteConfirmationDialogGenerator / deleteAllConfirmationDialogGenerator / editConfirmationDialogGenerator / saveConfirmationDialogGenerator |
the relevant item(s) | produce the confirmation message/dialog content |
processDataBeforeExport |
export rows | transform rows before CSV export |
processDataBeforePDFExport |
export rows + jsPdfDoc (jsPDF) |
(PDF export is partially stubbed; CSV is the supported path) |
utils helpers exposed to hooks/scripts β re-implement on a utils object passed into every hook:
utils.findFormElement(pathOrHiddenId)β returns the field config by itspathor itshiddenId.utils.getFlatArrayOfAllFormFields()β flat array of all form fields excluding grid fields.utils.getFlatArrayOfAllFormFieldsIncludingGridFields()β flat array including nested grid fields.utils.messageService.showInfoToast(msg, opts)/showSuccessToast(msg, {life})/showErrorToast(...)β toast notifications.utils.errorsMap[path] = 'message'β set a validation error on another field by path (reflect onto that reactive control's errors).window.top.postMessage({ eventType, eventData }, '*')β send a custom event to the parent (see Β§12).
globalData is the object pushed by the parent via SET_GLOBAL_DATA_TO_USE_IN_ANY_SCRIPT; pass it into every hook scope. queryParams are the URL query params.
5. Top-level grid configuration β PrimeNG p-table
Build a reusable <um-grid> around p-table. Map every key below. Defaults are stated; apply them when the key is absent.
5.1 Grid-level keys
| UI Maker key | Type / default | PrimeNG / Angular equivalent | Notes |
|---|---|---|---|
grid.header |
string | grid title/caption header | |
grid.minWidth |
default '50rem' |
[style]="{'min-width': minWidth}" on <p-table> |
|
grid.breakpoint |
default '40rem' |
[breakpoint] (responsive) |
|
grid.currentPageReportTemplate |
e.g. 'Showing {first} to {last} of {totalRecords} entries' |
[currentPageReportTemplate] + paginator report |
|
grid.showColumnSelector |
default false |
p-multiSelect of columns toggling visible columns |
|
grid.dataFieldPath |
string | nested grid only: pick the data array from this field path | not for top-level grid |
grid.selectFieldsForQuery |
projection {field:1\|-1} (default all DB fields) |
API Maker query select; pass into the data-fetch request |
not for nested grids |
grid.sortMode |
'single'\|'multiple', default 'single' |
[sortMode] |
'multiple' requires the meta key when adding columns |
grid.multiSortMeta |
[{field, order:1\|-1}] |
[multiSortMeta] |
each field must have enableSorting |
5.2 Columns β grid.fields[] (IDBMasterConfigGridField)
If grid.fields is empty/omitted: auto-generate columns from the schema in schema order, EXCLUDING the Mongo _id column.
| Per-column key | Equivalent | Notes |
|---|---|---|
header |
<th> text |
|
path |
cell value path; supports dotted (country.name) |
resolve nested |
enableSorting |
pSortableColumn="path" + <p-sortIcon> |
|
visibilityStatus |
EDBMasterConfigGridColumnVisibilityStatus.INVISIBLE \| VISIBLE |
initial column visibility (used with column selector) |
autoFormatDate |
format the cell as a date | default format dd-MM-yyyy hh:mm:ss a (date-fns); see Β§13 |
dateTimeFormat |
per-column date format override | date-fns |
cssClass |
CSS class on the cell/column | |
dbData (FK lookup) |
{ collection, pkField, displayField } |
resolve the stored key (e.g. _id) to a display value via a lookup; in nested-grid columns use dataSource:'db_data' + dbData:{pkField, displayField} |
dataSource |
'db_data' in nested-grid columns |
resolve FK display |
5.3 Pagination β grid.pagination
| Key | Default | Equivalent |
|---|---|---|
showPagination |
false |
[paginator] |
serverSidePagination |
false |
[lazy]="true" + (onLazyLoad) server fetch; NOT supported for nested grids |
rowsPerPage |
10 |
[rows] |
rowsPerPageOptions |
[10,20,50,100,500,1000] |
[rowsPerPageOptions] |
5.4 Selection β grid.selection
| Key | Default | Equivalent |
|---|---|---|
mode |
'multiple' |
single β selectionMode="single"; multiple β "multiple" |
dataKey |
auto from schema (PK) | [dataKey] |
metaKeySelection |
true |
[metaKeySelection] (true = needs Ctrl/Cmd) |
checkboxSelection |
false (multiple only) |
<p-tableHeaderCheckbox> + <p-tableCheckbox> column |
5.5 Filter / search β grid.filter
| Key | Default | Equivalent |
|---|---|---|
showGlobalSearch |
false |
global filter input |
globalSearchFields |
string[] (dotted paths ok) |
string β contains, number β exact match; pass to [globalFilterFields] and/or server query |
rowFilter |
false |
per-column <p-columnFilter> |
5.6 Grouping β grid.grouping (rowGroupMode 'subheader' only)
groupRowsBy β [rowGroupMode]="'subheader'" + [groupRowsBy]; headerStyle/headerCssClass (group header <tr>); footerStyle/footerCssClass; footerColumnPath (field used to render the group footer). rowspan grouping is not supported.
5.7 Row operations β grid.operations
| Key | Default | Equivalent / behavior |
|---|---|---|
delete |
{ enable, cssClass, apiCallOverrides, apiCallOverridesDeleteMany } |
row delete button (icon styled by cssClass, e.g. text-danger) |
edit |
{ enable, cssClass, apiCallOverrides, apiCallOverridesGetById } |
row edit button (e.g. text-info); edit fetches single record via apiCallOverridesGetById |
hideDeleteConfirmation / hideEditConfirmation |
suppress per-row confirm dialog | |
hideDeleteManySuccessMessage |
false |
suppress bulk-delete success toast (then you must show it manually) |
disableDoubleClickForEdit |
(default: double-click row opens edit) | when set, disable double-click-to-edit |
disableDeleteButtonClickForDelete |
(default: delete button deletes) | when set, delete button does not delete |
gridOperationsOrder |
EDBMasterGridOperationsOrder[] = delete \| edit \| custom (only these 3) |
order of the row action buttons |
gridColumnsOrder |
EDBMasterGridColumnsOrder[] = checkbox \| dbFields \| actions (only these 3) |
order of column groups |
actionColumnWidth |
e.g. '120px' |
width of the actions column |
customActionButtons[] |
{ actionName, cssClass (icon e.g. 'pi pi-check'), style (e.g. {color:'yellow'}), confirmationMessageScript } |
per-row custom button. confirmationMessageScript is a JS template-literal string evaluated with selectedGridItems in scope, e.g. "`Do you want to approve ${selectedGridItems.name}?`" |
Per-row action hiding (magic flags on the ROW DATA object) β when rendering each row's buttons, honor:
____hide_delete_button, ____hide_edit_button, ____hide_view_button, and ____hide_<actionName> (custom) β if true on the row object, hide that button for that row only.
5.8 Toolbar operations β top-level operations
| Key | Default | Equivalent |
|---|---|---|
operations.add |
IDBMasterConfigOperation { enable, label, apiCallOverrides } |
Add button β opens add form dialog; title 'Add ${screenName}' |
operations.hideAddConfirmation |
suppress add confirm | |
operations.hideDeleteAllButton |
false |
hide Delete-All button |
operations.hideDeleteAllConfirmation |
suppress delete-all confirm | |
operations.showRefreshButton |
Refresh button (reload grid) | |
operations.exportData |
see Β§10 | Export button |
operations.importData |
see Β§10 | Import button |
operations.customActionButtons[] |
IDBMasterTopLevelCustomActionButton[] |
toolbar custom buttons (distinct from row ones) |
operations.operationsOrder |
EDBMasterOperationsOrder[] |
order toolbar items; default order: theme_selector, column_selector, global_search, import, export, refresh, add, custom_actions, delete_all |
5.9 Grid data fetch override
grid.apiCallOverrides (IDBMasterAPICallOverrides) overrides the get-grid-data API (see Β§9). Note: in the grid (non-form) context, formData and allDropdownDataMap are not available to the override hooks.
IDBMasterConfigGridField,IDBMasterConfigOperation,IDBMasterCustomActionButton, andIDBMasterTopLevelCustomActionButtonare not given standalone interface blocks in the docs β infer their exact shapes from the page's actual config and the worked examples here.
6. Form framework β Angular Reactive Forms
6.1 Layout
form.fields is a 2-D array (outer = rows, inner = columns). A field that itself has a nested fields (also 2-D) and no control is a pure layout container β render its children but bind no value for it. form.width sets the dialog (p-dialog) width. The form runs in three modes: add, edit, view (view = read-only).
6.2 Shared base field props (IDBMasterConfigFormField) β present on EVERY control
| Prop | Type / default | Handling |
|---|---|---|
hiddenId |
string | element id for programmatic access (utils.findFormElement) |
label |
string | field label (may be ' ' to reserve space for alignment) |
helpText |
HTML string | render below the control via [innerHTML] (sanitized) |
path |
dotted string | the nested FormControl key where the value is stored |
control |
EDBMasterFormControl |
which control component to render |
cssClassDiv |
default col-lg mt-4 col-md-{calc by columns.length} |
wrapper div classes |
autofocus |
default false |
focus on form open |
disabled |
boolean \| JS-expression string |
static OR evaluated expression (see Β§6.3) |
visible |
boolean \| JS-expression string, default true |
static OR evaluated expression |
fields |
IDBMasterConfigFormField[][] |
nested 2-D layout |
validations |
Pick<IPropertyValidation,'required'> & { requiredFun?: string } |
required (boolean) + requiredFun (dynamic expression) |
validationErrors.required |
string | custom required error message |
Build the FormGroup from all field paths (nested groups for dotted paths). Add/remove Validators.required per validations.required. Set [autofocus]. Render helpText HTML below each control. Container fields and value-less controls (button, image, customHTML, divider, accordion, tab_view) must NOT be registered as value-bearing FormControls unless they actually carry a path.
6.3 JS-expression-string evaluation (visible / disabled / requiredFun)
visible, disabled, and requiredFun may each be a boolean OR a JavaScript expression string evaluated against form data, e.g. "formData.type === 'readonly'", "formData.userRole === 'admin'", "formData.type === 'individual' ? true : false".
- Implement a single
expression.service.tsevaluator. Build the expression scope:{ formData, queryParams, globalData, utils, column, config }. - Recommended safe approach: a constrained
new Function('formData','queryParams','globalData','utils','column','config', 'return (' + expr + ');')invoked with those args, wrapped in try/catch. Do not useeval. (If your security posture forbidsnew Function, swap in a sandbox like a small expression interpreter, but the string contract must be honored.) - Re-evaluate reactively on
form.valueChanges(and onglobalDatachanges). Bindvisibleβ*ngIf/[hidden];disabledβcontrol.disable()/enable()(and[disabled]for non-control elements). requiredFuntakes precedence over staticrequiredwhen both are present: whenrequiredFunexists, ignore staticrequiredand dynamically add/removeValidators.requiredbased on the evaluated result on every value change.- The full
IPropertyValidationset exists at schema level (required, min, max, minLength, maxLength, unique [deprecated], email, validatorFun, enum). Most controls only exposerequired/requiredFunviavalidations, but control-specific validators appear in settings (e.g.inputTextSettings.minLength,inputNumberSettings.min/max/minLength,editorSettings.maxLength). Map each to the matching AngularValidators.*; map a customvalidatorFunto a custom validator; mapenumto a membership validator.
7. Per-control conversion reference (THE BIG SECTION β all 24 controls)
For every control: render the listed PrimeNG component bound to a FormControl at path (support dotted paths); honor the shared base props (Β§6.2); apply style as [ngStyle], cssClass as styleClass, tooltip/tooltipPosition/tooltipStyleClass via pTooltip; wire the control's jsCode hooks to the matching PrimeNG outputs. The jsCode $scope (IDBMasterUIPageUtilsScope) for input-like controls is { formData, column, allDropdownDataMap, globalData, utils, queryParams, config, event } (data-source controls additionally get reqBody, dropdownData, reloadDropdownsOfPath). Code may be a string, a function, or a userUtils.* reference, may mutate data, return a Promise (await), or return a function (invoke).
7.1 input β p-inputText (pInputText on <input>)
- Settings (
inputTextSettings):style,placeholder,autocomplete(defaultoff),spellcheck(defaultfalse),maxLength(βmaxlengthattr +Validators.maxLength),minLength(βValidators.minLength),tooltip/tooltipPosition/tooltipStyleClass,jsCode,validationErrors.minLength(custom minLength message). - Validation:
required/requiredFun;minLength(custom msg viainputTextSettings.validationErrors.minLength);maxLength. - Events (
EDBMasterInputTextAppendTo):visible,disabled,ngModelChange,focus,blur,keyUp,keyDown.
7.2 textarea β pInputTextarea on <textarea>
- Settings (
textAreaSettings):style,placeholder,rows,autoResize(defaultfalse),maxLength,tooltip*,jsCode. - Validation:
required/requiredFun;maxLength. (No minLength/email/pattern exposed.) - Events (
EDBMasterTextAreaAppendTo):visible,disabled,ngModelChange,focus,blur,keyUp,keyDown.
7.3 password β p-password
- Settings (
inputPasswordSettings):style,cssClass,placeholder,maxLength,tooltip*,jsCode. (toggleMask,feedback, prompt/strength labels referenced in examples β map top-password[toggleMask]/[feedback]/labels.) - Validation:
required/requiredFun;maxLength. - Events (
EDBMasterInputPasswordAppendTo):visible,disabled,ngModelChange,focus,blur,keyUp,keyDown.
7.4 inputNumber β p-inputNumber
- Settings (
inputNumberSettings):style,cssClass,placeholder,min,max,minFractionDigits,mode('decimal'\|'currency'\|''),currency(ISO 4217 β required whenmode='currency', no default),locale(default'en-US'),prefix,suffix,showButtons,buttonLayout(default'stacked'; alsohorizontal/vertical),step,useGrouping,maxLength,minLength,tooltip*,jsCode,validationErrors.minLength. - Validation:
required/requiredFun;min/max(PrimeNG constraint);minLength/maxLength;minFractionDigits. - Events (
EDBMasterInputNumberAppendTo):visible,disabled,ngModelChange,focus,blur,keyUp,keyDown.
7.5 inputMask β p-inputMask
- Settings (
inputMaskSettings):style,cssClass,mask(a=alpha,9=numeric,*=alphanumeric,?=optional remainder, literals( ) - / . , :; e.g.'(999) 999-9999','99/99/9999','99:99 aa'),placeholder,slotChar(default_),autoClear(defaulttrueβ clears incomplete value on blur),maxLength,tooltip*,jsCode. - Validation:
required/requiredFun;maxLength; mask enforces format/length;autoClearenforces completeness. - Events (
EDBMasterInputMaskAppendTo):visible,disabled,ngModelChange,focus,blur,complete,keyUp,keyDown. - GOTCHA: modifying the model value inside the
blurhook does NOT work (PrimeNG bug) β route model-mutating logic to thecompletehook.
7.6 inputOtp β p-inputOtp
- Settings (
inputOtpSettings):mask(boolean β obscure values, password-style),integerOnly(numeric only),uiControlWidth(default300px),length(number of boxes / OTP length),tooltip*. - Validation:
required/requiredFun;integerOnly;length(fixed OTP length). - Events: none control-specific; only evaluation-based
visible/disabled/requiredFun. Auto-focus between boxes is built-in.
7.7 date_picker β p-calendar (Calendar / DatePicker)
- Value is always a native JS
Datestored atpath(write back aDate, not ISO string). - Settings (
datePickerSettings):style,cssClass,placeholder,showSeconds,showTime,hourFormat('12'\|'24', default'24'),dateTimeFormat,timeOnly,minDate(Date),maxDate(Date),view('date'\|'month'\|'year', default'date'),tooltip*,jsCode. - Mapping:
showTimeβ[showTime],showSecondsβ[showSeconds],hourFormatβ[hourFormat],timeOnlyβ[timeOnly],minDate/maxDateβ[minDate]/[maxDate],viewβ[view],dateTimeFormatβ[dateFormat](convert date-fns tokens to PrimeNG calendar tokens, e.g.dd-MM-yyyyβdd-mm-yy; see Β§13). - GOTCHA: for
view='month'setdateTimeFormat='MM-yyyy'; forview='year'set'yyyy'.minDate/maxDateare selection constraints only β add equivalent validators if true validation is needed. - Validation:
required/requiredFun. - Events (
EDBMasterDatePickerAppendTo):visible,disabled,ngModelChange(ononSelect/value change),focus,blur.
7.8 checkbox β p-checkbox (binary)
- Bind a boolean FormControl;
[binary]="true". - Settings (
checkboxSettings):tooltip*,jsCode(onChange). - Validation:
required/requiredFun. - Events (
EDBMasterCheckboxAppendTo):ngModelChange(a.k.a. onChange β(onChange)),visible,disabled.
7.9 radio β p-radioButton (one per option, single FormControl)
- Settings (
radioSettings):displayType('inline'= horizontal one line,'new_line'= vertical),displayInCenter(center the group),options[](REQUIRED):{ label, value }(value any type: string/boolean/number β selected option'svalueis stored),tooltip*,jsCode. - Validation:
required/requiredFun. - Events (
EDBMasterRadioAppendTo):ngModelChange,visible,disabled.
7.10 dropdown β p-dropdown (data-source control β see Β§8)
- Settings (
dropdownSettings):style,cssClass,placeholder,showClear(defaultfalse),dataSource(REQUIRED:'static_data'\|'db_data'\|'api_call'),staticData[],dbData(Partial<{instance,database,collection,table}> & {find,select,limit,deep,sort}βselectmay be CSV string OR object map{field:1}; identity may be inherited from schema),optionLabel(default'label', HTML supported β use item template with[innerHTML]),optionValue(default'value', stored value),filter(defaultfalse),filterBy(comma-separated, no spaces),filterMatchMode(default'contains'; alsostartsWith\|endsWith\|equals\|notEquals\|in\|lt\|lte\|gt\|gte),alwaysGetLatestDataOnFormOpen(defaultfalse),virtualScroll(defaultfalse),reloadDropdownsOfPath[],isDependentOnPath[],tooltip*,jsCode,addNewFormConfig(IDBMasterConfigβ inline add modal),apiCallOverrides. - Validation:
required/requiredFun. - Events (
EDBMasterDropdownAppendTo):visible,disabled,modifyDropdownRequest(mutatereqBodybefore fetch),onceDropdownDataLoaded(post-processdropdownData),onChange.
7.11 auto_complete β p-autoComplete (data-source control β see Β§8) (v1.11+)
- Stores the selected option's
optionLabelvalue (there is NOoptionValueβ it is commented out; the whole label-field value is stored). - Settings (
autocompleteSettings):style,cssClass,placeholder,showClear,minLengthForSearch(βminLength),delay(debounce ms),forceSelection(clears manual input not matching a suggestion),dataSource,staticData[],dbData,optionLabel(default'label', HTML supported),filterBy,filterMatchMode(contains\|startsWith\|endsWithonly),alwaysGetLatestDataOnFormOpen,virtualScroll,reloadDropdownsOfPath[],isDependentOnPath[],dropdown(show dropdown button),maxLength,tooltip*,addNewFormConfig,apiCallOverrides,jsCode. completeMethod: fire only afterminLengthForSearchchars, debounced bydelay; fordb_data/api_callhonorisDependentOnPath(skip until all parent paths have values).- Validation:
required/requiredFun;forceSelection;maxLength. - Events (
EDBMasterAutoCompleteAppendTo):onSelect,visible,disabled,modifyAutoCompleteRequest,onceAutoCompleteDataLoaded,focus,blur,keyUp,keyDown,onShow,onHide,onClear,onDropdownClick.
7.12 multi_select β p-multiSelect (data-source control β see Β§8)
- Settings (
multiselectSettings):style,cssClass,placeholder,showClear,minLength,delay,dataSource,staticData[],dbData,optionLabel(default'label', HTML),optionValue(default'value'),showHeader,filter,filterBy,filterMatchMode(default'contains'; full set),alwaysGetLatestDataOnFormOpen,virtualScroll,maxSelectedLabels,selectionLimit,selectedItemsLabel(e.g.'{0} items selected'),showToggleAll,optionDisabled(field marking an option disabled),optionGroupLabel+optionGroupChildren(grouping),display('comma'\|'chip'),reloadDropdownsOfPath[],isDependentOnPath[],addNewFormConfig,apiCallOverrides,tooltip*,jsCode. - Validation:
required/requiredFun;selectionLimit. - Events (
EDBMasterMultiSelectAppendTo):visible,disabled,modifyMultiSelectRequest,onceMultiSelectDataLoaded,onChange(+ panel show/hide/clear as applicable).
7.13 color_picker β p-colorPicker
- Settings (
colorPickerSettings):style,cssClass,format('hex'\|'rgb'\|'hsb'),tooltip*,jsCode. - Validation:
required/requiredFun. - Events (
EDBMasterColorPickerAppendTo):visible,disabled,ngModelChange.
7.14 editor β p-editor (Quill WYSIWYG) (v1.14.0+)
- Value is HTML rich text; round-trip and sanitize on render.
- Settings (
editorSettings):style(e.g.{height:'350px'}),placeholder,maxLength(no native maxlength β enforce viaValidators.maxLengthand/or guard inonTextChange),tooltip*,formats[](whitelist of allowed Quill formats from:background, bold, color, font, code, italic, link, size, strike, script, underline, blockquote, header, indent, list, align, direction, code-block, image, video, clean; configure the Quill toolbar/formats whitelist β when omitted use full default toolbar),jsCode. - Validation:
required/requiredFun;maxLength. - Events (
EDBMasterEditorAppendTo):visible,disabled,ngModelChange,onTextChange,onInit,onSelectionChange,focus,blur,keyUp,keyDown.
7.15 rating β p-rating
- Settings (
ratingSettings):cancel(defaulttrueβ show clear icon),stars(number of stars = max value),iconOnClass/iconOnStyle,iconOffClass/iconOffStyle,iconCancelClass/iconCancelStyle,onIconHTML/offIconHTML/cancelIconHTML(raw custom HTML icons, e.g.<img>or<i>β override class/style),tooltip*,jsCode. - Validation:
required/requiredFun. - Events (
EDBMasterRatingAppendTo):ngModelChange,onRate,onCancel,onFocus,onBlur,visible,disabled.
7.16 knob β p-knob
- Settings (
knobSettings):min,max,step,size(px diameter, e.g.150,200),strokeWidth,valueColor,rangeColor,textColor,valueTemplate(e.g.'{value}%','{value}Β°C'β{value}substituted),cssClass,style,tooltip*,jsCode. - Validation:
required/requiredFun;min/max/stepenforced by knob. - Events (
EDBMasterKnobAppendTo):ngModelChange,visible,disabled.
7.17 file_upload β p-fileUpload (full spec)
- Stored value at
path= array of{ originalName: string, uploadPath: string }objects (extra props allowed). NOT rawFileobjects. - Settings (
fileUploadSettings):style,cssClass,auto(defaultfalse;trueauto-uploads after selection, else show upload button),uploadApiUrl(REQUIRED),downloadApiUrl,removeApiUrl,multiple,accept(e.g.'image/*','.pdf,.doc,.docx'),maxFileSize(default10000000= 10MB),fileLimit,tooltip*. (_showUploadButton,_fileSelectEventare internal β do not expose; you may need equivalent internal state.) - Contracts (resolve
:beHostPort/:userPathplaceholders first β see Β§9): - UPLOAD: POST files in multipart field named exactly
filestouploadApiUrl; response is[{originalName, uploadPath}, ...]β becomes the FormControl value. Usep-fileUploadcustomUpload/uploadHandler. - DOWNLOAD/PREVIEW: call
downloadApiUrlβ returns base64; use for inline preview (data URI for images) and download. - REMOVE: call
removeApiUrlwith a single{ originalName, uploadPath }payload, then update the FormControl array. - Validation:
required/requiredFun;maxFileSize;fileLimit;accept. - No data-source / cascading behavior.
7.18 image β p-image (display-only; <img> inside <span>)
- Settings (
imageSettings):src(REQUIRED; may start empty and be set dynamically via jsCode fromformData, e.g.column.imageSettings.src = formData.profilePicture),style,cssClass,imageParentSpanClass(class on the wrapping<span>),width,height,alt,preview(fullscreen preview on click),previewImageSrc(separate high-res preview source, distinct from thumbnailsrc),tooltip*,jsCode. - Display-only β do NOT register as a value FormControl unless a
pathis set. Often paired withfile_uploadto preview uploaded images. Re-render whensrcchanges. - Events (
EDBMasterImageAppendTo):visible,disabled,click.
7.19 button β p-button (action only, no value)
- Render as a standalone
<p-button>; do NOT add it as a value-bearing FormControl. - Settings (
buttonSettings):label(required, can be empty for icon-only),style,cssClass,link,icon,iconPos(default'left';right/top/bottom),loading,loadingIcon,severity(primary/success/info/warning/danger),raised,rounded,text,outlined,badge,badgeClass(newer PrimeNG:badgeSeverity),size('small'\|'large'),tooltip*,jsCode(click). - Events (
EDBMasterButtonAppendTo):click(β(onClick)),visible,disabled. Click code commonly usesutils.messageService.showSuccessToast(...)andutils.errorsMap[path]='message'.
7.20 divider β p-divider (presentational, no value)
- Settings (
dividerSettings):style,cssClass,align(center/left/rightfor horizontal;top/center/bottomfor vertical β[align]),type(dashed/dotted/solidβ[type]),dividerText(projected content; or use fieldlabel). - No control-specific events; only
visible/disabled(evaluated).
7.21 customHTML β <div [innerHTML]> + DomSanitizer (no PrimeNG component)
- Settings (
customHTMLSettings):htmlCode(REQUIRED) β raw HTML rendered via[innerHTML]. Sanitized (XSS protection β some tags/attributes/event-handlers/scripts stripped). Use AngularDomSanitizer.sanitize(NOTbypassSecurityTrust) to preserve the same protective behavior. Keep inline style attributes intact where possible. - No value, no FormControl. Primary dynamic feature is
visible.
7.22 accordion β p-accordion (field container, no value)
- Settings (
accordionSettings):style,cssClass,activeIndex(number | number[]),defaultIndex(default0; seedactiveIndexif absent),multiple(β[multiple]; in multiple modeactiveIndexisnumber[]),jsCode,tabs[]: each{ style, header (HTML β render with[innerHTML]), headerStyle, headerCssClass, disabled (**boolean OR JS-expression string**), fields (2-D nested fields) }. - Recursively render each tab's
fields[][]as their own reactive controls; the accordion contributes no value of its own. - Events (
EDBMasterAccordionAppendTo):visible,disabled,activeIndexChange.
7.23 tab_view β p-tabView + p-tabPanel (field container, no value)
- Settings (
tabViewSettings):activeIndex,defaultIndex(default0),style,cssClass,jsCode,tabs[]:{ style, header (HTML), headerStyle, headerCssClass, disabled (**boolean only** here), fields (2-D nested fields) }. - Events (
EDBMasterTabViewAppendTo):visible,disabled,activeIndexChange(β(onChange)/(activeIndexChange)).
7.24 grid (nested) β p-table + add/edit p-dialog backed by a FormArray of FormGroups
- Value is an array stored at
pathβ model as aFormArray<FormGroup>; bind to<p-table>[value]; edit/add via ap-dialoghosting the nested form. - Settings (
gridSettings) =Omit<IDBMasterConfig,'schema'>β a full child config (its ownoperations,grid,form,jsCode,externalLibs,cssCode,apiCallOverrides...). Recurse: a nested grid insidegridSettings.formis anotherFormArray+p-table+ dialog. Unlimited nesting. For nested grids, locate the child array viadataFieldPath;serverSidePaginationandselectFieldsForQueryare NOT supported for nested grids. - Reproduce all grid features (Β§5), row magic-hide flags, the Add modal flow (
operations.add.enable/label; on save push the FormGroup value into the FormArray; respecthideAddConfirmation), edit (open dialog with row), delete (remove from array; respect confirmations/disableDoubleClickForEdit/disableDeleteButtonClickForDelete),db_datacolumn FK resolution (collection/pkField/displayField β display value), and nested-form dropdown loading (re-fetch on dialog open whenalwaysGetLatestDataOnFormOpen). - Lifecycle hooks (
gridSettings.jsCode):oncePageLoad,oncePageLoadWithContext,gridRender,modifyGridRequest,preSaveAndEdit(mode),beforeSaveModalOpen,beforeEditModalOpen(see Β§4.2). - Nested form fields carry the full
IPropertyValidation(required, min, max, minLength, maxLength, email, validatorFun, enum) β convert each to Angular validators.
8. Dependent / cascading dropdowns
Cascading is NOT automatic. It requires, on the relevant dropdownSettings/autocompleteSettings/multiselectSettings, all three of:
reloadDropdownsOfPath: string[]β on the parent control. When the parent's value changes, the child controls at these paths are cleared and reloaded.isDependentOnPath: string[]β on the child control. The child loads data only when all listed parent paths have values informData.jsCodemodify{Dropdown|AutoComplete|MultiSelect}Requestβ on the child. MutatesreqBodyto inject the parent value into the query, e.g.reqBody.find.continentId = formData.continentId;.
N-level example: planet β continent β country β state
// continent depends on planet, reloads country on change
{ path:'continentId', control:'dropdown', dropdownSettings:{
dataSource:'db_data', dbData:{ collection:'ui_maker_continent' }, optionValue:'_id', optionLabel:'name',
isDependentOnPath:['planetId'], reloadDropdownsOfPath:['countryId'],
jsCode:[{ appendTo:'modifyDropdownRequest', code:`reqBody.find.planetId = formData.planetId;` }]
}}
Angular re-implementation:
- For each control with
reloadDropdownsOfPath, subscribe to its FormControlvalueChanges. On change: for each child path, reset the child's value and trigger its reload (clear cached options and re-fetch). - For each control with
isDependentOnPath, gateloadOptions()behind a check that every listed parent path has a non-empty value informData; otherwise leave options empty/disabled. - Before each child fetch, run the
modify*Requesthook against a mutablereqBodyobject so it can inject parent values (reqBody.find.<parent> = formData.<parent>), then send that body to the data service. - Run
once*DataLoadedafter the fetch to post-processdropdownData. - Cascading mixes freely across
dropdown/auto_complete/multi_select. The chain can be arbitrarily deep β reset and reload transitively down the chain.reloadDropdownsOfPathis also available as a mutable variable inside hooks: pushing a path into it triggers that path's reload.
9. API layer & overrides
Replace API Maker's auto-generated calls and all apiCallOverrides with an explicit api.service.ts (HttpClient).
IDBMasterAPICallOverrides:
interface IDBMasterAPICallOverrides {
url: string; // REQUIRED; supports :beHostPort and :userPath tokens
headers?: any; // custom request headers
method?: 'POST'; // ONLY 'POST' is supported
body?: any; // custom request body
pkField?: string; // PK field name; '_id' for MongoDB (delete/getById/deleteMany)
codeBeforeAPICall?: string; // JS hook: mutate request before call
codeAfterAPICall?: string; // JS hook: mutate response after call
}
- All override calls are POST. Implement only POST.
- Placeholder substitution (do before every call):
:beHostPortβ backend protocol+host+port (e.g.https://example.com);:userPathβ the admin user path segment. codeBeforeAPICall/codeAfterAPICallscope:config,formData,allDropdownDataMap,apiResponse(after only),globalData,headers,reqBody,utils,queryParams,mode. Note: in the grid context,formDataandallDropdownDataMapare NOT available.
Per-operation overrides to implement (each points to a custom endpoint):
| Override path | Operation | Extra |
|---|---|---|
operations.add.apiCallOverrides |
create/save | |
operations.importData.apiCallOverrides |
CSV import persist | default uses save API |
grid.apiCallOverrides |
get grid data | |
grid.operations.delete.apiCallOverrides |
delete by id | uses pkField |
grid.operations.delete.apiCallOverridesDeleteMany |
bulk delete | uses pkField |
grid.operations.edit.apiCallOverrides |
edit/update by id | |
grid.operations.edit.apiCallOverridesGetById |
fetch single record before edit | uses pkField |
dropdownSettings/autocompleteSettings/multiselectSettings.apiCallOverrides |
dropdown options (dataSource:'api_call') |
map response via optionLabel/optionValue |
When no override exists, call the standard API Maker endpoint for that operation; build query bodies (find, select, limit, deep, sort) via query.service.ts using the API Maker IQueryFormat shape (selectFieldsForQuery projection {field:1|-1}).
10. CSV import & export
10.1 Import β operations.importData (IDBMasterImportData)
interface IDBMasterImportData {
enable: boolean;
icon?: string; // default 'pi pi-file-import'
gridWidth?: string; // import preview grid width, e.g. '2000px'
fieldMappings: IDBMasterImportGridColumn[];
apiCallOverrides?: IDBMasterAPICallOverrides; // default: the save API
}
interface IDBMasterImportGridColumn {
header: string; // UI preview grid header
sourceField: string; // CSV column name (e.g. 'NAME', 'DOB')
targetField: string; // DB field path (e.g. 'name', 'dateOfBirth')
convertTargetFieldToType: EType; // convert CSV value to this type before save (e.g. string, date)
dateParseFormat?: string; // date-fns parse format (default ISO)
dateTimeZone?: string; // tz to convert parsed date into before DB query
dateFormatUIDisplay?: string; // date format for UI display in preview
dateTimeZoneUIDisplay?: string; // tz for UI display in preview
width?: string; // per-column preview width (e.g. '300px')
filterType?: EDBMasterConfigFilterType; // CALCULATED/internal β do not set
}
Implement: a file picker β parse CSV with Papa Parse (available globally as Papa) β render an editable preview p-table (columns from fieldMappings, widths from width/gridWidth) β for each row map sourceFieldβtargetField, convert to convertTargetFieldToType (use date-fns parse with dateParseFormat; convert tz via date-fns-tz with dateTimeZone; display in preview using dateFormatUIDisplay/dateTimeZoneUIDisplay) β POST to the save API (or apiCallOverrides).
10.2 Export β operations.exportData
operations: { exportData: { showExportButton: true, icon: 'pi pi-download',
csv: { enable: true, labelCSV: 'CSV', labelCSVSelected: 'CSV Selected' } } }
| Key | Default | Behavior |
|---|---|---|
showExportButton |
show Export button | |
icon |
'pi pi-download' |
button icon |
csv.enable |
enable CSV export | |
csv.labelCSV |
'CSV' |
"export all" label |
csv.labelCSVSelected |
'CSV Selected' |
"export selected rows" label |
Provide export all and export selected actions. Run the processDataBeforeExport hook to transform rows first. (PDF export is partially stubbed via processDataBeforePDFExport + jsPdfDoc β CSV is the fully supported path.)
11. Theming
theme= anEDBMasterThemevalue mapping directly to a PrimeNG prebuilt theme stylesheet name (e.g.md-dark-deeppurpleβ PrimeNGmd-dark-deeppurple). Families include Bootstrap4, MD (Material), MDC, Fluent, Lara, Soho, Viva, Mira, Nano, Saga, Vela, Arya, Nova, Luna, Rhea β with light/dark variants and color accents (blue/indigo/purple/teal/green/orange/amber/pink). Load the corresponding PrimeNG theme CSS at runtime.showThemeSelector(defaultfalse) β render a theme switcher in the grid header that swaps the loaded theme stylesheet (theme.service.ts).- Global defaults to carry:
dateTimeFormat(default'dd-MM-yyyy hh:mm:ss a'),dateTimeZone(default browser tz).
12. Iframe / parent-app integration
UI Maker pages run in an iframe and communicate via window.postMessage with { eventType, eventData }. Implement parent-integration.service.ts to send/receive these. Handshake: iframe emits PAGE_READY; parent sets config (append-query/global data) then sends PARENT_READY. With wait-for-parent-ready=true, the page blocks until PARENT_READY.
Query params (currently from URL; if not iframe-embedded, take as @Input/route/query params): admin-path, db-master-name, x-am-authorization, x-am-user-authorization, x-am-internationalization, wait-for-parent-ready, show-logs.
Iframe β Parent events (IDBMasterEventFromIframeToParent): PAGE_READY, RECORD_SAVED, RECORDS_IMPORTED, RECORD_UPDATED, RECORD_DELETED, MANY_RECORD_DELETED, GRID_EXPORTED, GRID_REFRESHED, ADD_NEW_BUTTON_CLICKED, CUSTOM_ACTION_BUTTON_CLICKED, DROPDOWN_ADD_NEW_RECORD_SAVED. Plus custom events via window.top.postMessage({ eventType, eventData }, '*').
Parent β Iframe events (IDBMasterEventFromParentToIframe): TRIGGER_ADD_NEW_BUTTON_CLICK, TRIGGER_REFRESH_BUTTON_CLICK, DATA_TO_APPEND_IN_GRID_LOAD_FIND_QUERY (merge into grid find query), DATA_TO_APPEND_IN_RECORD_SAVE_API_PAYLOAD (merge into create payload), DATA_TO_APPEND_IN_RECORD_UPDATE_API_PAYLOAD (merge into update payload), SET_GLOBAL_DATA_TO_USE_IN_ANY_SCRIPT (populate globalData), PARENT_READY.
The data layer must support merging an externally-supplied object into (a) the grid load/find query, (b) the save payload, and (c) the update payload. If replacing the iframe with normal Angular: expose @Input() appendToFindQuery, @Input() appendToSavePayload, @Input() appendToUpdatePayload, @Input() globalData, @Output() emitters for each iframeβparent event, and methods triggerAddNew() / triggerRefresh(). Preserve wait-for-parent-ready semantics (don't load until inputs are set) and show-logs. (For real iframe binding in Angular, sanitize the URL with DomSanitizer.bypassSecurityTrustResourceUrl.)
13. Date & timezone handling
- All dates use date-fns formats. Global
dateTimeFormatdefault'dd-MM-yyyy hh:mm:ss a'; globaldateTimeZonedefault browser tz. Dates are converted todateTimeZonebefore grid display AND before being sent into the DB query (use date-fns-tz). - Grid columns:
autoFormatDate:trueformats the cell (default'dd-MM-yyyy hh:mm:ss a'); per-columndateTimeFormat/dateTimeZoneoverride. date_pickervalue is a JSDate. PrimeNG calendar format caveat: PrimeNG'sdateFormattoken syntax differs from date-fns β convert (e.g.dd-MM-yyyyβdd-mm-yy). PrimeNG calendar has no time format token, so time is auto-appended to the displayed value. Unsupported PrimeNG calendar tokens will break β validate during conversion.- For
view='month'/'year', keepdateTimeFormatconsistent ('MM-yyyy'/'yyyy'). - Centralize all of this in
date-format.service.ts(date-fns format string, date-fns β PrimeNG token conversion, tz conversion).
14. Step-by-step conversion procedure (per UI page)
For each UI Maker page:
- Read the page's
dbMasterConfigin full β it is authoritative. List every key actually used (don't port unused keys). - Scaffold the page component set under
pages/<db-master-name>/(.ts/.html/.scss,.main.ts,.api.ts,.model.ts). Import the shared library. - Build the
FormGroupfromform.fields[][]: create nestedFormGroups for dottedpaths; lay out rows/columns withcssClassDiv(computecol-md-*from columns count). - Wire validations: static
requiredβValidators.required; control-specific (minLength,maxLength,min,max,email,enum, customvalidatorFun) β matching validators;requiredFunβ dynamic validator re-evaluated onvalueChanges(takes precedence over staticrequired). SurfacevalidationErrors.*messages. - Wire dropdowns/cascades: implement
dataSource(static/db/api),optionLabel/optionValue(HTML labels via templates),filter/filterBy/filterMatchMode,virtualScroll,alwaysGetLatestDataOnFormOpen,addNewFormConfig, andreloadDropdownsOfPath+isDependentOnPath+modify*Requestcascades (Β§8). - Wire file uploads (Β§7.17): upload/download/remove contracts, placeholder substitution, response shape.
- Port
jsCodehooks: page-level (Β§4.2) β component methods/lifecycle; per-control β control event handlers. ResolveuserUtils.Main.*(page) anduserUtils.GeneralUtils.*(shared). Support string/function/reference, Promise, returned-function. - Port
cssCodeβ page<name>.scss; common CSS β globalstyles.scss. - Wire the API service: standard endpoints + every
apiCallOverrides(POST only, placeholder substitution, before/after hooks). - Wire the grid (Β§5): columns, sorting, filtering, global search, pagination, grouping, selection, row actions + ordering + magic-hide flags, toolbar ops + ordering, custom action buttons +
confirmationMessageScript. - Wire CSV import/export (Β§10) and theme (Β§11).
- Wire parent integration (Β§12): postMessage events /
@Input/@Outputbridge, append-query/save/update payload merging,globalData. - Verify against the original running page (Β§15) β behavior, not just structure.
15. Definition of Done / verification checklist
Tick every item for the page. This is the safety net for "do not miss any feature."
Form controls β every control present in the config is rendered correctly: - [ ] input, textarea, password, inputNumber, inputMask, inputOtp, date_picker, checkbox, radio - [ ] dropdown, auto_complete, multi_select (all three data sources: static_data / db_data / api_call) - [ ] color_picker, editor (Quill formats whitelist), rating (custom icons), knob - [ ] file_upload, image, button, divider, customHTML (sanitized), accordion, tab_view, nested grid
Per-control fidelity:
- [ ] Every settings property mapped (no property dropped)
- [ ] optionLabel/optionValue correct (auto_complete stores label-value, no optionValue)
- [ ] filterMatchMode set per control (autoComplete only contains/startsWith/endsWith)
- [ ] HTML in optionLabel/helpText/headers rendered via [innerHTML]
- [ ] inputMask model mutation done in complete, not blur
- [ ] inputNumber currency requires currency; locale default en-US; buttonLayout default stacked
- [ ] date_picker value is Date; PrimeNG format conversion; month/year view formats; time auto-appended
Validation:
- [ ] static required, dynamic requiredFun (precedence), validationErrors messages
- [ ] control-specific minLength/maxLength/min/max/minFractionDigits/email/enum/validatorFun
- [ ] selectionLimit, forceSelection, maxFileSize, fileLimit, accept
Conditional logic:
- [ ] visible / disabled as boolean OR JS-expression string, re-evaluated on value changes
- [ ] requiredFun re-evaluated dynamically
- [ ] tab/accordion disabled (accordion: bool|expr; tabView: bool only)
Dependent / cascading dropdowns:
- [ ] reloadDropdownsOfPath, isDependentOnPath, modify*Request working N levels, mixed control types
- [ ] children cleared + reloaded; parent value injected into child query; once*DataLoaded runs
File upload:
- [ ] upload (files field, returns [{originalName, uploadPath}]), download/preview (base64), remove ({originalName, uploadPath})
- [ ] auto vs manual, multiple, accept, maxFileSize (10MB default), fileLimit, :beHostPort/:userPath
Grid:
- [ ] columns (auto-from-schema excludes _id), header/path/dotted paths, sorting, visibilityStatus, autoFormatDate/dateTimeFormat, dbData FK lookup, cssClass
- [ ] sortMode/multiSortMeta, pagination (showPagination/serverSide/rowsPerPage/options/report template), grouping (subheader), selection (mode/dataKey/metaKeySelection/checkboxSelection)
- [ ] filter (showGlobalSearch/globalSearchFields string-contains/number-exact, rowFilter), selectFieldsForQuery, minWidth/breakpoint, column selector
- [ ] row ops (edit/view/delete) + cssClass + confirmations + double-click-edit + delete-button-click + gridOperationsOrder/gridColumnsOrder/actionColumnWidth
- [ ] per-row magic-hide flags (____hide_delete_button/_edit_/_view_/____hide_<actionName>)
- [ ] row custom action buttons + confirmationMessageScript (template-literal eval with selectedGridItems)
- [ ] toolbar ops + operationsOrder (default order), add/refresh/delete-all/export/import/column-selector/theme-selector + hide flags + hideDeleteManySuccessMessage; top-level custom action buttons
Nested structures:
- [ ] nested fields[][] layout containers; nested grid (gridSettings) as FormArray, unlimited depth; dataFieldPath; addNewFormConfig inline add flow
API & overrides:
- [ ] standard endpoints + all apiCallOverrides (add/save, edit, getById, delete, deleteMany, import, grid get-data, dropdown) β POST only
- [ ] :beHostPort/:userPath substitution; codeBeforeAPICall/codeAfterAPICall with correct scope vars; pkField
CSV / theming / dates:
- [ ] CSV import (Papa Parse, fieldMappings, type conversion, date parse/tz, UI display formats, preview grid widths) + export (all/selected, labels, icon, processDataBeforeExport)
- [ ] theme applied + showThemeSelector; global dateTimeFormat/dateTimeZone defaults; date-fns β PrimeNG conversion
JS / CSS hooks:
- [ ] page-level jsCode hooks (all EDBMasterConfigAppendTo used) with correct scope (config, globalData, utils, queryParams, gridEvent, gridData, reqBody, formData, mode)
- [ ] per-control jsCode events wired to PrimeNG outputs; string/function/userUtils.* refs; Promise/returned-function support
- [ ] utils helpers (findFormElement, getFlatArrayOfAllFormFields[IncludingGridFields], messageService.*, errorsMap)
- [ ] common CSS β global styles; page CSS β page styles; common JS β GeneralUtils; page JS β Main
- [ ] external libs (externalLibs.css/js CDNs loaded before user code); inbuilt libs available: Papa, dateFns, dateFnsTz, _, moment; Bootstrap 5.3.3 grid+utilities
Parent integration:
- [ ] all iframeβparent and parentβiframe events; handshake (PAGE_READY/PARENT_READY); wait-for-parent-ready; show-logs
- [ ] append-data merging into find query / save payload / update payload; SET_GLOBAL_DATA_TO_USE_IN_ANY_SCRIPT β globalData; query params
When any item cannot be satisfied from the config alone, inspect the original UI Maker page at runtime and reproduce the observed behavior exactly. Do not skip, do not approximate.