---
name: "form-building"
description: "Building create/edit forms with FormField components, validation integration, and save patterns"
---
# Form Building Skill
## Core Principles
- **MobX state management** - NO state hooks or other libraries unless specified
- **Group related fields** using `DetailFieldContainer`
- **Ask first** if unclear about field importance
- Forms are for CREATE and EDIT operations
## Available Components
Located in: `ui/src/common/components/form/fields/`
### Basic Fields
- `FormFieldText` - Short text fields
- `FormFieldTextArea` - Long text fields
- `FormFieldCheckbox` - Boolean 0/1 fields
- `FormFieldReadOnly` - Display-only fields
- `FormFieldDate` - Date picker
- `FormFieldColor` - Color picker
- `FormFieldCodeEdit` - Code editor
### Single Select (Constants)
```typescript
import { constants } from "@/models/constants";
```
### Multi Select (Constants)
```typescript
```
### Model Relationships
**Small lists** (< 50 options):
```typescript
record={props.asset}
field="organization_id"
label="Organization"
placeholder="Select Organization"
modelName="organization"
modelSearchField="q"
modelDisplayField="label"
modelSearchFilters={{ disabled: "0" }}
/>
```
**Large lists** (use search):
```typescript
record={props.asset}
field="organization_id"
label="Organization"
placeholder="Search Organization"
modelName="organization"
modelSearchField="q"
modelDisplayField="label"
modelSearchFilters={{ disabled: "0" }}
/>
```
**Multi-select (small lists)**:
```typescript
record={props.asset}
field="tag_ids"
label="Tags"
placeholder="Select Tags"
modelName="tag"
modelSearchField="q"
modelDisplayField="label"
/>
```
**Multi-select (large lists with search)**:
```typescript
record={props.asset}
field="tag_ids"
label="Tags"
placeholder="Search Tags"
modelName="tag"
modelSearchField="q"
modelDisplayField="label"
/>
```
## Model Relationship Best Practices
- **Always try to use `label` as modelDisplayField**
- **Always try to use `q` as modelSearchField**
- If model doesn't have a good label, modify it in `XXModel.ts`
- Assume all models exist - don't create new ones
## JSONB Fields Pattern
For nested JSONB objects:
```typescript
// JSONB class must extend ValidationClass
```
**Note**: Unlike DetailFields, FormFields don't need `parentRecord` or `SafeBaseModel` wrapper
## Grouping Fields with Container
```typescript
record={props.asset}
field="organization_id"
label="Organization"
modelName="organization"
modelSearchField="q"
modelDisplayField="label"
/>
```
## Complete Form Example
```typescript
import { observer } from "mobx-react-lite";
import { AssetModel } from "@/models/asset";
import { OrganizationModel } from "@/models/organization";
import { constants } from "@/models/constants";
import {
DetailFieldContainer,
FormFieldText,
FormFieldTextArea,
FormFieldSelect,
FormFieldModelSearchSelect,
FormFieldCheckbox,
FormFieldDate,
} from "@/ui/common/components/form/fields";
import { Button } from "@/ui/shadcn/ui/button";
interface AssetFormProps {
asset: AssetModel;
onSave: () => void;
onCancel: () => void;
}
export const AssetForm = observer((props: AssetFormProps) => {
const handleSave = async () => {
await props.asset.save();
props.onSave();
};
return (
record={props.asset}
field="organization_id"
label="Organization"
modelName="organization"
modelSearchField="q"
modelDisplayField="label"
modelSearchFilters={{ disabled: "0" }}
/>
);
});
```
## Validation Integration
Forms automatically connect to model validation rules:
```typescript
// Model already has validation rules defined
get validationRules() {
return assetValidationRules;
}
// Form fields automatically show errors
```
## Save Pattern
```typescript
const handleSave = async () => {
try {
await props.asset.save();
// Success - redirect or close modal
props.onSave();
} catch (error) {
// Error handling - validation errors show on fields automatically
console.error("Save failed:", error);
}
};
```
## Key Differences from Details View
| Feature | Form Fields | Detail Fields |
| ------------ | ------------- | -------------------- |
| Use case | Create/Edit | View/Edit |
| JSONB | Direct record | Needs `parentRecord` |
| Type casting | Not needed | `SafeBaseModel` |
| Save button | Required | Optional |
| Validation | Built-in | Built-in |
## Key Rules
1. Always create components with `#ui_code_tools`
2. Use search variants for large option lists (>50 items)
3. Constants are model-specific: `constants.{model}.{field}`
4. Forms handle validation automatically via model rules
5. Call `model.save()` to persist changes
6. Ask before implementing if field importance is unclear
7. JSONB fields don't need `parentRecord` in forms