Skip to content

Rent a car: Submitting the form

The reservation button currently fires the special 'submit' event, which runs form-level validation but doesn’t notify your application code. Let’s wire it to a real handler instead, so we can read the data and confirm the booking.

Step 1 — Emit a custom event from the button

Section titled “Step 1 — Emit a custom event from the button”

Replace onClick: 'submit' with on: { click: 'handleReservation' }. The 'submit' literal triggers form-level validation and emits the event only when the form is valid; for arbitrary event names, use the legacy on: { click: '<eventName>' } shape.

In your formEvent callback, match the event name and read event.data:

function handleFormEvent(event) {
if (event.name === 'handleReservation') {
console.log('Reservation:', event.data);
alert(`Reserved ${event.data.car} from ${event.data.collectOffice}`);
}
}
<FormComponent
formDef={formDef}
formConfig={formConfig}
formEvent={handleFormEvent}
/>
05-final.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dropdown('car', {
labelField: 'label',
valueField: 'id',
itemRenderer: 'carItemRenderer',
inputDebounce: 300,
label: 'Select car',
validator: {
type: 'string',
required: true,
},
onLoad: 'loadCars',
onFilter: 'filterCars',
}),
gui.inputs.dropdown('collectOffice', {
labelField: 'label',
valueField: 'id',
items: [
{
id: 'lhr',
label: 'London Heathrow',
},
{
id: 'cdg',
label: 'Paris CDG',
},
{
id: 'fra',
label: 'Frankfurt Main',
},
],
label: 'Collect from office',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.booleanInput('differentReturn', {
label: 'Choose a different return location',
}),
gui.inputs.dropdown('returnOffice', {
labelField: 'label',
valueField: 'id',
items: [
{
id: 'lhr',
label: 'London Heathrow',
},
{
id: 'cdg',
label: 'Paris CDG',
},
{
id: 'fra',
label: 'Frankfurt Main',
},
],
label: 'Return to office',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.rangeCalendar('rentalDates', {
label: 'Rental dates',
validator: {
type: 'array',
required: true,
minItems: 2,
maxItems: 2,
},
}),
gui.inputs.radiogroup('rentalType', {
options: [
{
label: 'Daily',
value: 'daily',
},
{
label: 'Weekly',
value: 'weekly',
},
{
label: 'Monthly',
value: 'monthly',
},
],
label: 'Rental type',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.booleanInput('driverOver25', {
label: 'Driver aged over 25',
validator: {
type: 'boolean',
const: true,
required: true,
},
}),
gui.inputs.booleanInput('hasDiscountCode', {
label: 'I have a discount code',
}),
gui.inputs.textInput('discountCode', {
label: 'Discount code',
validator: {
type: 'string',
required: true,
minLength: 4,
},
}),
gui.actions.button({
label: 'Reserve',
uid: 'submit',
onClick: 'handleReservation',
}),
];

Fill the form, click Reserve, and the JSON payload arrives in your formEvent callback.

You’ve now built a real-world form that uses:

  • declarative widget composition with gui.layouts.grid and gui.layouts.flex,
  • validators per field,
  • named states with include rules,
  • a custom item renderer,
  • async data loading with onLoad/onFilter and inputDebounce,
  • form events to capture the submit.