Appearance
Record Behaviors: Examples & Best Practices
This page provides complete script examples for common use cases, along with architectural best practices and troubleshooting tips to help you write efficient, secure, and maintainable Record Behaviors.
Complete Script Examples
These examples demonstrate how to solve real-world business problems using the Record Behavior APIs. All examples use the modern, self-documenting function signature.
Example 1: E-commerce Order Form
Goal: Automatically calculate the total price of an order. For bulk orders (10 or more items), reveal a "Discount Code" field and make the total_price field read-only to prevent manual overrides.
javascript
export default async ({ $form }) => {
// This logic runs in the browser on 'load' and 'update' to provide a real-time UI.
const quantity = $form('quantity').value() || 0;
const unitPrice = $form('unit_price').value() || 0;
// Rule 1: Conditionally show the discount field for bulk orders.
const isBulkOrder = quantity >= 10;
$form('discount_code').visible(isBulkOrder);
// Rule 2: Make total price read-only for bulk orders to enforce automated pricing.
$form('total_price').editable(!isBulkOrder);
// Rule 3: Recalculate the total price only when relevant fields change.
// Using $form.updated() is a critical performance optimization.
if ($form.event === 'load' || $form.updated('quantity', 'unit_price')) {
$form('total_price').set(quantity * unitPrice);
}
}Example 2: Project Management Task
Goal: When creating a new task, set a default due_date to 7 days in the future. On submit, validate that the due date is not in the past, ensuring this rule is enforced on both the client and the server.
javascript
export default async ({ $form }) => {
// The 'load' event runs for NEW records on both client and server,
// guaranteeing a default due date is always set.
if ($form.event === 'load' && !$form('due_date').value()) {
const today = new Date();
const dueDate = new Date(today.setDate(today.getDate() + 7));
// Format as YYYY-MM-DD for the date input
$form('due_date').set(dueDate.toISOString().split('T'));
}
// The 'submit' event runs authoritatively on the server for every create and
// update operation, guaranteeing data integrity.
if ($form.event === 'submit') {
const dueDateValue = $form('due_date').value();
if (dueDateValue) {
const dueDate = new Date(dueDateValue);
const today = new Date();
today.setHours(0, 0, 0, 0); // Normalize to the start of today for accurate comparison
if (dueDate < today) {
// Setting an error during 'submit' will block the save operation.
$form('due_date').error('The due date cannot be in the past.');
} else {
// Best Practice: Always explicitly clear the error if validation passes.
$form('due_date').error(null);
}
}
}
}Architectural Best Practices
Writing effective Record Behaviors involves more than just correct syntax. It's about leveraging the platform's architecture to build logic that is secure, performant, and maintainable.
Embrace the Sandbox. Your code runs in a secure WASM environment. You must not attempt to use any environment-specific APIs like
window,document, orfetch(). Rely exclusively on the provided{ $form, $db, $user, $ai }APIs. This constraint is a feature, guaranteeing your logic is portable and secure.Design for Dual Execution. Logic in
loadandsubmitevents runs on both the client and the server. Write your code to be stateless and deterministic. Avoid any logic that depends on an environment-specific state that might differ between the client and server.Use the Right Event for the Job.
load: Only for setting default values on new records. This is your initialization hook.update: Only for real-time UI feedback. This code does not run on the server. Use it for calculations and dynamic form visibility that enhance the user experience.submit: Only for final, authoritative validation. This is your data integrity gatekeeper. It's the last line of defense that runs on the server before a record is saved.
Be Specific with
$form.updated(). Inupdateevents, always wrap your logic in anif ($form.updated('field_a', 'field_b'))block. This is the single most important performance optimization for client-side scripts, ensuring your logic only runs when relevant fields change.Keep It Cohesive. A Record Behavior should be responsible for the logic of a single table. If you find yourself writing complex logic that spans multiple business objects, consider creating a server-side
Logicand triggering it via adb_event.
Troubleshooting
Script Not Running at All?
- Ensure your
record_behaviorrecord is marked asis_active. - Verify it is linked to the correct
table. - Remember, only one behavior script can be active per table.
- Ensure your
Default Values Not Being Set on the Backend?
- Your default value logic must be inside an
if ($form.event === 'load')block. Theloadevent is the designated hook for setting defaults on both the client and the server for new records.
- Your default value logic must be inside an
UI is Slow or "Janky" When Typing?
- You are likely missing an
if ($form.updated(...))guard in yourupdateevent logic. Without it, your code runs for every change on the form, even irrelevant ones.
- You are likely missing an
How to Debug?
- Use
console.log()liberally inside your script. Logs from client-side execution will appear in your browser's developer console. Server-side logs will appear in your backend service's logs. - In your user profile settings, enable Dev Mode for more detailed, verbose logging from the form system in the browser console. This can help you trace the execution flow and inspect payloads.
- Use