Add form value persistence across cache revalidation re-renders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.9 KiB
Executable File
name, description
| name | description |
|---|---|
| ajax-error-handling | Handling Ajax errors in RSX including response formats, error codes, client-side error display, and Form_Utils. Use when implementing error handling for Ajax calls, working with response_error(), form validation errors, or debugging Ajax failures. |
RSX Ajax Error Handling
Response Architecture
RSX returns HTTP 200 for ALL Ajax responses (success and errors). Success/failure is encoded in the response body via _success field.
// Success response
{
"_success": true,
"_ajax_return_value": { /* your data */ }
}
// Error response
{
"_success": false,
"error_code": "validation|not_found|unauthorized|auth_required|fatal",
"reason": "User-friendly message",
"metadata": { /* field errors for validation */ }
}
Rationale: Batch requests need uniform status codes. Always get parseable response body. Non-200 only means "couldn't reach PHP at all".
Error Codes
| Constant | Purpose |
|---|---|
Ajax::ERROR_VALIDATION |
Field validation failures |
Ajax::ERROR_NOT_FOUND |
Resource not found |
Ajax::ERROR_UNAUTHORIZED |
User lacks permission |
Ajax::ERROR_AUTH_REQUIRED |
User not logged in |
Ajax::ERROR_FATAL |
Uncaught PHP exceptions |
Constants available in both PHP (Ajax::ERROR_*) and JavaScript (Ajax.ERROR_*).
Server-Side: Returning Errors
Use response_error() helper - never return _success manually:
#[Ajax_Endpoint]
public static function save(Request $request, array $params = []) {
// Validation error with field-specific messages
if (empty($params['email'])) {
return response_error(Ajax::ERROR_VALIDATION, [
'email' => 'Email is required'
]);
}
// Not found error
$user = User_Model::find($params['id']);
if (!$user) {
return response_error(Ajax::ERROR_NOT_FOUND, 'User not found');
}
// Success - just return data (framework wraps it)
$user->name = $params['name'];
$user->save();
return ['id' => $user->id];
}
Let Exceptions Bubble
Don't wrap database/framework operations in try/catch. Let exceptions bubble to the global handler:
// WRONG - Don't catch framework exceptions
try {
$user->save();
} catch (Exception $e) {
return ['error' => $e->getMessage()];
}
// CORRECT - Let it throw
$user->save(); // Framework catches and formats
When try/catch IS appropriate: File uploads, external API calls, user input parsing (expected failures).
Client-Side: Handling Errors
Ajax.js automatically unwraps responses:
_success: true→ Promise resolves with_ajax_return_value_success: false→ Promise rejects with Error object
try {
const user = await User_Controller.get_user(123);
console.log(user.name); // Already unwrapped
} catch (error) {
console.log(error.code); // 'validation', 'not_found', etc.
console.log(error.message); // User-displayable message
console.log(error.metadata); // Field errors for validation
}
Automatic Error Display
Uncaught Ajax errors automatically display via Modal.error():
// No try/catch - error shows in modal automatically
const user = await User_Controller.get_user(123);
Form Error Handling
With Rsx_Form (Recommended)
const result = await Modal.form({
title: 'Add User',
component: 'User_Form',
on_submit: async (form) => {
try {
const result = await Controller.save(form.vals());
return result; // Success - close modal
} catch (error) {
await form.render_error(error);
return false; // Keep modal open
}
}
});
form.render_error() handles all error types:
- validation: Shows inline field errors + alert for unmatched errors
- fatal/network/auth: Shows error in form's error container
With Form_Utils (Non-Rsx_Form)
try {
const result = await Controller.save(form_data);
} catch (error) {
if (error.code === Ajax.ERROR_VALIDATION) {
Form_Utils.apply_form_errors($form, error.metadata);
} else {
Rsx.render_error(error, '#error_container');
}
}
Form_Utils.apply_form_errors():
- Matches errors to fields by
nameattribute - Adds
.is-invalidclass and inline error text - Shows alert ONLY for errors that couldn't match to fields
Error Display Methods
Modal.error() - Critical Errors
await Modal.error(error, 'Operation Failed');
Red danger modal, can stack over other modals.
Rsx.render_error() - Container Display
Rsx.render_error(error, '#error_container');
Rsx.render_error(error, this.$sid('error'));
Displays error in any container element.
Developer vs Production Mode
Developer Mode (IS_DEVELOPER=true):
- Full exception message
- File path and line number
- SQL queries with parameters
- Stack trace (up to 10 frames)
Production Mode:
- Generic message: "An unexpected error occurred"
- No technical details exposed
- Errors logged server-side
Common Patterns
Simple Validation
#[Ajax_Endpoint]
public static function save(Request $request, array $params = []) {
$errors = [];
if (empty($params['email'])) {
$errors['email'] = 'Email is required';
}
if (empty($params['name'])) {
$errors['name'] = 'Name is required';
}
if ($errors) {
return response_error(Ajax::ERROR_VALIDATION, $errors);
}
// ... save logic
}
Check Specific Error Type
try {
const data = await Controller.get_data(id);
} catch (error) {
if (error.code === Ajax.ERROR_NOT_FOUND) {
show_not_found_message();
} else if (error.code === Ajax.ERROR_UNAUTHORIZED) {
redirect_to_login();
} else {
// Let default handler show modal
throw error;
}
}
More Information
Details: php artisan rsx:man ajax_error_handling