Skip to main content

Form

The Form component in GridStudio enables developers to build rich, reactive forms using a wide variety of atomic and composite UI elements. It supports built-in input validation, file uploads, dynamic dropdowns, AJAX-based option loading, and custom submission logic.

Each form is defined using a sdk.options.items structure, where each item corresponds to a form element. You can group items using arrays to create multi-column layouts and use ReactiveVar bindings to connect external state or editors.

The form also supports lifecycle event handlers, such as onSubmit, enabling full control over data collection, validation, transformation, and submission.


Usage Example

CustomFunction(async function ({ sdk }, reject, resolve) {
resolve(async function CustomFunction({ sdk, templateInstance }) {
const current = sdk.env.current.get()

const coverPhotoDropzone = new ReactiveVar()
const photosDropzone = new ReactiveVar()
const descriptionEditor = new ReactiveVar()

const selectCurrencyOptions = new ReactiveVar([])

sdk.utils.loading.show()

selectCurrencyOptions.set(
current.currencies.map((currency) => ({
value: currency.isoCode,
label: currency.isoCode,
selected: currency.isDefault,
}))
)

sdk.options = {
data: null,
items: [
{
type: 'atoms-input',
name: 'title',
text: 'Title',
rules: { required: true },
},
{
type: 'atoms-editor',
name: 'description',
text: 'Description',
editor: descriptionEditor,
rules: { required: true },
},
[
{
type: 'atoms-dropzone',
name: 'coverPhotoDropzone',
text: 'Cover Photo',
dropzone: coverPhotoDropzone,
options: { acceptedFiles: 'image/*', maxFiles: 1 },
},
{
type: 'atoms-dropzone',
name: 'photosDropzone',
text: 'Photos',
dropzone: photosDropzone,
options: { acceptedFiles: 'image/*', maxFiles: 50 },
},
],
{
type: 'atoms-select',
name: 'currencyIsoCode',
text: 'Currency',
selectOptions: selectCurrencyOptions,
rules: { required: true },
},
{
type: 'atoms-button',
text: 'Create Property',
buttonType: 'submit',
},
],
events: {
onSubmit: async function (event, templateInstance) {
event.preventDefault()
sdk.utils.loading.show()

const title = event.target.title.value
const description = descriptionEditor.get().getData()
const currencyIsoCode = event.target.currencyIsoCode.value

const obj = { title, description, currencyIsoCode }

const coverPhotoUploads = await sdk.utils.uploadAsync(coverPhotoDropzone)
obj.coverPhotoUrl = coverPhotoUploads?.[0]?.url

const photosUploads = await sdk.utils.uploadAsync(photosDropzone)
obj.photosUrls = photosUploads.map((f) => f.url)

await sdk.customFunction.callAsync(
{ pathString: sdk.utils.absolutePathString('../../methods/properties/create.js') },
obj
)

event.target.reset()
sdk.utils.loading.remove()
sdk.utils.drawer.hide()
},
},
}

sdk.utils.loading.remove()
})
})

Key Concepts

FeatureDescription
itemsAn array of form elements (or arrays of elements for grouped layout)
rulesField-level validation rules such as required
ReactiveVarReactive variables for managing dynamic field states or bindings
atoms-* typesAtomic UI components like input, select, dropzone, editor, etc.
components-* typesComposite or layout components (e.g., title separators)
select-ajaxDynamically populated dropdowns with search and pagination
onSubmitHandler for form submission logic, executed when submit button is clicked
sdk.utils.uploadAsyncHandles file uploads and returns URLs

Best Practices

  • Use ReactiveVar for fields with dynamic state, such as editors or file uploaders.
  • Define form sections using components-composite-title to group related fields.
  • Use sdk.utils.loading.show() and sdk.utils.loading.remove() to provide feedback during async actions.
  • For AJAX selects, handle pagination and filtering manually using the params object.