# How Embabel Form Generation and Binding Works with FormGenerator

> Discover how Embabel's FormGenerator and FormBinder create type-safe forms from Kotlin/Java classes, map submitted values, and perform automatic conversion and validation.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: how-to-guide
- Published: 2026-08-10

---

**Embabel uses `SimpleFormGenerator` to reflectively inspect Kotlin or Java classes and build UI `Form` objects, then employs `FormBinder` implementations to map submitted values back to type-safe instances with automatic conversion and validation.**

The `embabel-agent` repository provides a declarative framework for turning data classes into interactive forms without manual UI code. By combining reflection-based generation with annotation-driven binding, the system bridges the gap between backend models and frontend controls while maintaining compile-time safety.

## Form Generation with SimpleFormGenerator

The generation phase transforms annotated classes into structured `Form` objects containing UI controls. According to the source code in `embabel-agent-api`, this process centers on the `FormGenerator` interface and its concrete implementation.

### The FormGenerator Interface

In [`embabel-agent-api/src/main/kotlin/com/embabel/ux/form/FormGenerator.kt`](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-api/src/main/kotlin/com/embabel/ux/form/FormGenerator.kt), the `FormGenerator` interface declares the contract for form creation:

```kotlin
interface FormGenerator {
    fun <T : Any> generateForm(dataClass: KClass<T>, title: String): Form
}

```

The `SimpleFormGenerator` object in [`SimpleFormGenerator.kt`](https://github.com/embabel/embabel-agent/blob/main/SimpleFormGenerator.kt) provides the standard implementation, using Kotlin reflection to inspect class properties and Java fields.

### Property Inspection and Control Creation

`SimpleFormGenerator.getPropertiesInDeclarationOrder` obtains properties in the same order as underlying Java fields, guaranteeing stable UI ordering. The generation follows this sequence:

1. **Collect properties** – Reflective inspection gathers all class properties
2. **Filter fields** – Removes properties annotated with `@NoFormField` or non-nullable constructor parameters with default values (like auto-generated timestamps)
3. **Create controls** – `createControlForProperty` examines each property's Kotlin type (`String`, `Int`, `Boolean`, `LocalDate`, etc.) and returns appropriate controls like `TextField`, `Checkbox`, or `DatePicker`
4. **Append submit button** – Adds a final `Button` control automatically
5. **Return Form** – Packages controls with the supplied title into a `Form` instance

### Annotation-Based Customization

Developers control generation through annotations defined in [`SimpleFormGenerator.kt`](https://github.com/embabel/embabel-agent/blob/main/SimpleFormGenerator.kt):

- **`@FormField`** – Overrides the default control ID and maps form fields to constructor parameters
- **`@NoFormField`** – Excludes properties from the generated form (useful for server-side auto-populated fields)
- **`@Text`** – Provides human-readable labels and UI hints for controls

## Form Binding with FormBinder

After users submit forms, the binding phase reconstructs class instances from raw `ControlValue` objects. The `FormBinder` implementations in [`FormBinder.kt`](https://github.com/embabel/embabel-agent/blob/main/FormBinder.kt) handle type conversion, validation, and object instantiation.

### Java and Kotlin Binder Implementations

The `FormBinder<T>` interface defines `bind(submissionResult): T`, with two primary implementations:

- **`JavaFormBinder`** – Handles plain Java classes and records in [`FormBinder.kt`](https://github.com/embabel/embabel-agent/blob/main/FormBinder.kt)
- **`KotlinFormBinder`** – Handles Kotlin data classes, delegating to `JavaFormBinder` for non-data classes

The extension helper `bindTo` in the same file provides a convenient one-liner for binding `FormSubmissionResult` to target types.

### The Binding Flow

`FormBinder.formBinder` selects the appropriate implementation using Spring's `KotlinDetector`. The binding process then executes:

1. **Validation check** – Throws `ValidationException` if `FormSubmissionResult.valid` is false
2. **Path selection** – Routes to `KotlinFormBinder` for Kotlin types, otherwise `JavaFormBinder`
3. **Java binding** – For records, `bindJavaRecord` extracts components and invokes the canonical constructor; for plain classes, `bindJavaConstructor` iterates over constructors preferring those with more parameters
4. **Kotlin binding** – `bindKotlinDataClass` obtains the primary constructor, matches properties to parameters via `convertToParameterType`, and calls `constructor.callBy`
5. **Instance creation** – Returns fully populated, type-safe instances

### Type Conversion

Both binders contain conversion utilities that translate UI-specific `ControlValue` objects to target types:

- `convertToJavaType` and `convertJavaTextValue` for Java primitives
- `convertTextValue` and `convertNumberValue` for Kotlin types
- Support for dates, times, collections, and file IDs through `ControlValue` subclasses like `TextValue`, `NumberValue`, and `BooleanValue`

## End-to-End Example

This complete example demonstrates the generation and binding cycle:

```kotlin
// Define the data model with form annotations
data class CreateUser(
    @FormField("username") @Text(label = "User Name") val name: String,
    @FormField("age") val age: Int,
    @FormField("admin") val isAdmin: Boolean = false,
    @NoFormField val createdAt: java.time.Instant = java.time.Instant.now()
)

// Generate the form
val form: Form = SimpleFormGenerator.generateForm(
    dataClass = CreateUser::class,
    title = "Create New User"
)

// Simulate form submission (normally from UI layer)
val submission = FormSubmissionResult(
    valid = true,
    values = mapOf(
        "username" to ControlValue.TextValue("Alice"),
        "age"      to ControlValue.NumberValue(30.0),
        "admin"    to ControlValue.BooleanValue(true)
    ),
    validationErrors = emptyMap()
)

// Bind back to Kotlin object
val user: CreateUser = submission.bindTo<CreateUser>()

println(user)   // CreateUser(name=Alice, age=30, isAdmin=true, createdAt=...)

```

The `@FormField` annotations override default IDs for precise mapping, while `@NoFormField` keeps `createdAt` server-side only. The `FormBinder` automatically converts `ControlValue` objects to correct Kotlin types, respecting optional parameters and validation rules.

## Summary

- **`SimpleFormGenerator`** in [`SimpleFormGenerator.kt`](https://github.com/embabel/embabel-agent/blob/main/SimpleFormGenerator.kt) reflects over classes to build `Form` objects with appropriate controls for each property type
- **`FormBinder`** implementations in [`FormBinder.kt`](https://github.com/embabel/embabel-agent/blob/main/FormBinder.kt) reconstruct instances from submissions, with separate paths for Java records/classes and Kotlin data classes
- **Annotations** (`@FormField`, `@NoFormField`, `@Text`) provide declarative control over field visibility, IDs, and labels without boilerplate
- **Type conversion** utilities handle primitives, dates, and complex objects automatically when binding `ControlValue` to constructor parameters
- **Stable ordering** is guaranteed by `getPropertiesInDeclarationOrder`, which respects Java field declaration order

## Frequently Asked Questions

### How does Embabel handle nullable vs non-nullable fields in form generation?

`SimpleFormGenerator` inspects Kotlin type nullability when creating controls. Non-nullable constructor parameters with default values (such as timestamps or auto-generated IDs) are automatically excluded from the form via the filtering logic in `getPropertiesInDeclarationOrder`, while nullable fields remain optional in the UI.

### Can FormGenerator handle Java Records as well as Kotlin data classes?

Yes. `SimpleFormGenerator` processes both Java Records and Kotlin data classes through reflection. During binding, `JavaFormBinder.bindJavaRecord` specifically handles record components by matching them to submitted form values and invoking the canonical constructor, while `KotlinFormBinder` handles Kotlin-specific features like default arguments via `callBy`.

### What happens when form validation fails during binding?

If `FormSubmissionResult.valid` is false, the `FormBinder` throws a `ValidationException` containing the error map from `validationErrors`. This occurs before any type conversion or instantiation, ensuring that invalid submissions never attempt to create partial objects.

### Where can I find test examples of form binding in the embabel-agent repository?

Unit tests demonstrating both Java and Kotlin binding behavior are located in [`embabel-agent-api/src/test/kotlin/com/embabel/ux/form/FormBinderTest.kt`](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-api/src/test/kotlin/com/embabel/ux/form/FormBinderTest.kt). For generation testing, see [`SimpleFormGeneratorTest.kt`](https://github.com/embabel/embabel-agent/blob/main/SimpleFormGeneratorTest.kt) in the same directory, which validates control creation and property ordering.