FORMS_AND_WIDGETS(3) RSX Framework Manual FORMS_AND_WIDGETS(3) NAME Forms and Widgets - RSX form system with reusable widget components SYNOPSIS // Blade form markup // JavaScript - wire save button to form $('#save-btn').on('click', function() { const $form = $('.Rsx_Form').first(); $form.component().submit(); }); DESCRIPTION The RSX form system provides a clean separation between form structure (Rsx_Form), field layout (Form_Field), and input widgets (Text_Input, Select_Input, etc). This architecture enables: - Reusable widgets across all forms - Consistent validation error display - Automatic value collection and population - Test data generation via seeders - Read-only/disabled states - Custom field layouts without modifying widget code Key Components: - Rsx_Form: Container managing form submission and validation - Form_Field: Layout wrapper providing labels, help text, error display - Widgets: Reusable input components (Text_Input, Select_Input, etc) RSX_FORM COMPONENT The Rsx_Form component manages form data flow, submission, and validation. Required Attributes: $action - Controller method reference for form submission Example: Frontend_Clients_Controller.save Optional Attributes: $data - JSON-encoded object with initial form values Used for edit mode to populate fields Example: $data="{{ json_encode($client_data) }}" Methods: vals() - Get all form values as object vals(values) - Set all form values from object submit() - Submit form to $action endpoint seed() - Fill all fields with test data (debug mode only) Form Discovery: Rsx_Form automatically discovers all widgets using shallowFind('.Widget') and collects values based on their data-name attributes. No registration or manual wiring required. Example - Basic Form: Example - Edit Mode with Initial Data: @php $form_data = [ 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'email' => $user->email, ]; @endphp FORM_FIELD WRAPPER Form_Field provides consistent layout for labels, help text, and error display. It wraps a single widget and connects it to the form. Architecture: Form_Field extends Form_Field_Abstract, which provides all core functionality (widget discovery, data-name attributes, error handling). Form_Field adds visual formatting (labels, spacing, error display). - Form_Field_Abstract: Base class with field functionality, no formatting - Form_Field: Concrete implementation with Bootstrap formatting - Form_Hidden_Field: Specialized single-tag hidden input Required Attributes: $name - Field name for form serialization and error display Optional Attributes: $label - Label text displayed above field $required - Boolean, adds red asterisk to label $help - Help text displayed below field Responsibilities: - Display label with optional required indicator - Read data-name from child widget (set by Form_Input_Abstract) - Display validation errors returned from server - Provide consistent spacing and styling Example - Basic Field: Example - Required Field with Help Text: Example - Field with HTML in Label: Form_Field_Abstract: Use Form_Field_Abstract directly when you need field functionality without visual formatting (e.g., for custom layouts). Example - Unformatted Field: Form_Hidden_Field: Specialized single-tag component for hidden inputs. Unlike other Form_Field components, this IS both the field wrapper AND the hidden input itself (uses tag="input" type="hidden"). Example - Hidden Field: Form_Hidden_Field extends Form_Field_Abstract and implements val() to get/set the hidden input value. No child widget needed - the component itself is the input. Custom Layout: Form_Field can be extended or replaced with custom jqhtml to change field layout. The only requirement is that the child widget must have the data-name attribute set to the field name. Example - Horizontal Layout:
<%= content() %> <% if (this.has_error()) { %>
<%= this.get_error() %>
<% } %>
WIDGET INTERFACE All form widgets must implement the standard widget interface: Required: - CSS class "Widget" on root element - val() method for getting current value - val(value) method for setting value Optional: - seed() method for generating test data - Support for $disabled attribute Widget Responsibilities: 1. Value Management Widgets must implement getter/setter via val() method: val() { // Getter - return current value if (arguments.length === 0) { return this.$sid('input').val(); } // Setter - update value else { this.data.value = value || ''; this.$sid('input').val(this.data.value); } } 2. Disabled State Widgets should respect $disabled attribute: - Render with disabled HTML attribute - Display grayed-out appearance - Still return value via val() getter - Do not submit in HTML form (handled by browser) 3. Test Data (Optional) Widgets may implement seed() for debug mode: async seed() { if (this.args.seeder) { // Generate test data this.val('Test Value'); } } BUILT-IN WIDGETS Text_Input Basic text input supporting multiple types and textarea. Attributes: $type - Input type (text, email, url, tel, number, textarea) $rows - Number of rows for textarea (default: 3) $placeholder - Placeholder text $prefix - Text to prepend (creates input-group) $suffix - Text to append (creates input-group) $min - Minimum value for number inputs $max - Maximum value for number inputs $maxlength - Maximum length for text inputs $disabled - Disable input (grayed out, still returns value) $seeder - Seeder function name for test data Examples: Select_Input Dropdown select with options. Attributes: $options - Array of options (see below) $placeholder - Placeholder option text $disabled - Disable select (grayed out, still returns value) $seeder - Seeder function name for test data Options Format: Simple array: ['Option 1', 'Option 2', 'Option 3'] Object array: [ {value: 'opt1', label: 'Option 1'}, {value: 'opt2', label: 'Option 2'} ] From Blade: $options="{{ json_encode($options_array) }}" Examples: @php $industries = ['Technology', 'Finance', 'Healthcare']; @endphp @php $sizes = [ ['value' => 'sm', 'label' => 'Small (1-10)'], ['value' => 'md', 'label' => 'Medium (11-50)'], ['value' => 'lg', 'label' => 'Large (50+)'], ]; @endphp Checkbox_Input Checkbox with optional label. Attributes: $label - Label text displayed next to checkbox $checked_value - Value when checked (default: "1") $unchecked_value - Value when unchecked (default: "0") $disabled - Disable checkbox (grayed out, still returns value) Examples: Wysiwyg_Input Rich text editor using Quill. Attributes: $placeholder - Placeholder text $disabled - Disable editor (not yet implemented) $seeder - Seeder function name for test data Example: SEEDING TEST DATA The seed system generates realistic test data for forms during development. Enabled only when window.rsxapp.debug is true. Seed Button: Rsx_Form automatically displays a "Fill Test Data" button in debug mode. Clicking this button calls seed() on all widgets. Widget Seeding: Widgets implement seed() to generate appropriate test data: async seed() { if (this.args.seeder) { // TODO: Implement Rsx_Random_Values endpoint let value = 'Test ' + (this.args.seeder || 'Value'); this.val(value); } } Seeder Names: Specify seeder via $seeder attribute: Future Implementation: Planned Rsx_Random_Values endpoint will provide: - company_name() - Random company names - email() - Random email addresses - phone() - Random phone numbers - first_name() - Random first names - last_name() - Random last names - address() - Random street addresses - city() - Random city names DISABLED STATE Disabled widgets display as read-only but still participate in form value collection. IMPORTANT: Unlike standard HTML disabled elements (which don't submit their values in traditional HTML forms), RSX disabled fields DO return values when vals() is called and ARE included in Ajax form submissions. This makes disabled fields useful for displaying read-only data that should still be included in form submissions. Behavior: - Widget displays grayed-out appearance - User cannot interact with widget - val() getter still returns current value - Value included in form submission via Ajax (unlike HTML forms) - HTML disabled attribute prevents traditional browser form submission Example - Disable Individual Fields: Example - Conditional Disable: Use Cases: - Display data that cannot be edited - Show calculated or system-managed values - Enforce permissions (some users see but cannot edit) - Multi-step forms (disable completed steps) FORM SUBMISSION Form submission uses Ajax to send data to controller methods. JavaScript Submission: $('#save-btn').on('click', function() { const $form = $('.Rsx_Form').first(); const form_component = $form.component(); form_component.submit(); }); What Happens: 1. Form calls vals() to collect all widget values 2. Sends values to this.args.action via Ajax.call() 3. Handles response: - Success: Redirect if response.redirect provided - Validation errors: Display errors on fields - General errors: Log to console Controller Method: #[Ajax_Endpoint] public static function save(Request $request, array $params = []) { // Validation $validated = $request->validate([ 'email' => 'required|email', 'name' => 'required|string|max:255', ]); // Save data $user = User::create($validated); // Return response return [ 'success' => true, 'redirect' => Rsx::Route('Users_Controller', 'view', $user->id), ]; } Validation Errors: When validation fails, return errors in format: return [ 'success' => false, 'errors' => [ 'email' => 'The email field is required.', 'name' => 'The name field is required.', ], ]; Form automatically displays errors below each field. MULTI-COLUMN LAYOUTS Use Bootstrap grid for multi-column field layouts:
CREATING CUSTOM WIDGETS Create custom widgets by implementing the widget interface. Example - Rating Widget: File: rating_input.jqhtml
<% for (let i = 1; i <= 5; i++) { %> <% } %>
File: rating_input.js class Rating_Input extends Form_Input_Abstract { on_create() { this.data.value = 0; } on_ready() { const that = this; this.$.find('[data-rating]').on('click', function() { that.val($(this).data('rating')); }); } val(value) { if (arguments.length === 0) { return this.data.value; } else { this.data.value = value || 0; // Update star display this.$.find('[data-rating]').each(function() { const rating = $(this).data('rating'); $(this).toggleClass('bi-star-fill', rating <= value); $(this).toggleClass('bi-star', rating > value); }); } } async seed() { this.val(Math.floor(Math.random() * 5) + 1); } } Usage: EXAMPLES Complete Form Example: @php $form_data = isset($client) ? [ 'name' => $client->name, 'email' => $client->email, 'industry' => $client->industry, 'active' => $client->active, ] : []; $industries = ['Technology', 'Finance', 'Healthcare']; @endphp @if (isset($client)) @endif
SEE ALSO jqhtml(3), ajax(3), validation(3) RSX Framework October 2025 FORMS_AND_WIDGETS(3)