Skip to main content

Build a Dynamic Form Using Metadata

Overview

A Dynamic Form in WaveMaker renders its fields at runtime based on a metadata object, rather than being statically configured at design time. Use this approach when the fields your form needs to display are determined by a backend service or vary across contexts. By the end of this guide, you will have a fully functional dynamic form that reads field definitions from a variable and renders the appropriate components automatically.


Prerequisites

Before you begin, make sure you have:

  • A WaveMaker project open in Studio
  • A backend service (REST, Java, or imported API) that returns field metadata — or a Static Variable you can use to prototype

Step 1 — Prepare the Metadata Service and Variable

The Dynamic Form expects metadata in the following structure:

[
{
"name": "",
"displayname": "",
"type": "",
"required": "",
"widget": "",
"dataset": ""
}
]
PropertyDescription
nameField name (required)
displaynameLabel shown in the form UI for the field; used as the display name.
typeData type — defaults to string if omitted
requiredWhether the field is required
widgetComponent type used to render the field in the form
datasetOptions for dataset components (select, radioset, autocomplete, switch)
  1. Create or import the service that returns your field metadata.
  2. In WaveMaker Studio, go to Variables and create a new Service Variable bound to that service.
  3. Set the variable to call on page load so the metadata is available when the form renders.
tip

If your service returns data in a different structure, you do not need to change the service. Transform the data in the form's on-beforerender event — see Step 3.


Step 2 — Configure the Form Markup

  1. Drag a Form component onto your page canvas.
  2. Switch to the Markup tab.
  3. Add the metadata property to the <wm-form> tag and bind it to your variable's dataSet:
<wm-form
name="form"
captionposition="top"
captionalign="left"
enctype="application/x-www-form-urlencoded"
method="post"
metadata="bind:Variables.variableName.dataSet"
itemsperrow="xs-2 sm-2 md-2 lg-2"
show="true"
>
<wm-container
direction="row"
alignment="top-left"
wrap="true"
width="fill"
columns="2"
name="container2"
class="app-container-default"
variant="default"
></wm-container>
<wm-form-action
key="reset"
class="form-reset btn-default btn-filled"
iconclass="wi wi-refresh"
display-name="Reset"
type="reset"
></wm-form-action>
<wm-form-action
key="save"
class="form-save btn-primary btn-filled"
iconclass="wi wi-save"
display-name="Save"
type="submit"
></wm-form-action>
</wm-form>

Replace variableName with the name of the variable you created in Step 1.

note

When metadata is bound, the form ignores any statically added form fields and renders only what the metadata specifies at runtime.


Step 3 (Optional) — Transform Metadata with on-beforerender

If your service returns data in a structure that does not match the expected format, use the on-beforerender event to map it before the form renders.

  1. Add the on-beforerender attribute to the <wm-form> tag in markup:
<wm-form
name="formUserDetails"
metadata="bind:Variables.createUserDetails.dataSet"
on-beforerender="formUserDetailsBeforeRender($metadata, widget)"
>
  1. Open the Script tab and implement the handler. Return an array of field definition objects in the expected format:
Page.formUserDetailsBeforeRender = function($metadata, widget) {
return [
{
name: 'firstname',
displayname: 'First Name',
widget: 'select',
dataset: 'Eric,Brad,Sample,Amanda'
},
{
name: 'lastname',
displayname: 'Last Name'
},
{
name: 'age',
displayname: 'User Age',
type: 'integer',
widget: 'number'
},
{
name: 'gender',
displayname: 'Gender',
widget: 'radioset',
dataset: 'Male,Female'
},
{
name: 'phonenumber',
displayname: 'Phone Number'
}
];
};
tip

The $metadata parameter receives the raw data from your variable. Use it to derive field definitions dynamically instead of hardcoding them, so the form adapts automatically when your service data changes.


Step 4 — Handle Form Submission

To process the submitted data when the user clicks Save, implement the onBeforeSubmit event.

  1. Select the Form component on the canvas.
  2. In the Events panel, locate On Before Submit and click the handler icon to open the script editor.
  3. Perform validations or pre-submit logic before saving the form data. If the validation fails, return false to stop the submission; otherwise proceed with actions like invoking a Service Variable or navigating to another page after a successful save.
Page.formUserDetailsBeforeSubmit = function($event, widget, $data) {
if ($data.firstName == undefined) {
// Assumes that the Notification Action "notificationAction" is already created
Page.Actions.notificationAction.invoke({
"class": "error",
"message": "First name cannot be empty",
"position": "center center"
});

return false; // Stop the form submission
}
};

Field Properties Reference

Supported data types

big_decimal, big_integer, blob, boolean, byte, character, clob, date, datetime, double, float, integer, list, long, short, string, text, time, timestamp

Supported component types

autocomplete, checkbox, checkboxset, currency, date, datetime, number, password, radioset, rating, richtext, select, slider, switch, text, textarea, time, timestamp, upload

Common properties (all components)

PropertyDescription
placeholderPlaceholder text shown inside the input
hintTooltip hint for the field
tabindexTab order index
defaultvalueValue pre-filled when the form loads
requiredMarks the field as required
validationmessageCustom message shown on validation failure
readonlyMakes the field read-only
disabledDisables the field
showControls field visibility

Additional properties for dataset components

Applies to select, radioset, switch, and autocomplete:

PropertyDescription
datasetComma-separated values or a bound dataset
displayfieldField from the dataset to display as the label
datafieldField from the dataset to use as the submitted value
orderbySort order for dataset items