Class: Forms::Field
- Inherits:
-
Object
- Object
- Forms::Field
- Defined in:
- lib/forms/field.rb
Direct Known Subclasses
Constant Summary collapse
- TOKEN_JOINED_DATA_KEYS =
Data keys whose values are Stimulus token lists — merged by joining, not replacing, so validation controllers and live triggers coexist.
%i[controller action].freeze
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
Instance Method Summary collapse
-
#apply_validations(options) ⇒ Object
Merge a per-call
validate:override into the field's option hash. - #checkbox(*modifiers) ⇒ Object (also: #check_box)
-
#choices_select(choices = nil, *modifiers, **options) ⇒ Object
Enhanced select: choices.js-backed when searchable, native otherwise.
- #control(label: nil, hint: nil, required: false) ⇒ Object
- #field_id ⇒ Object
-
#field_label ⇒ Object
Humanized label text: the model's human_attribute_name when available.
- #field_name ⇒ Object
-
#field_value ⇒ Object
Ransack-safe getter: dispatches through method_missing consumers (e.g. Ransack::Search predicate getters) but only swallows the direct dispatch miss.
- #file(*modifiers) ⇒ Object
- #hidden ⇒ Object
-
#initialize(name:, model:, scope:, errors:, form:, error_name: nil) ⇒ Field
constructor
error_name: where errors live when it differs from the input's name — an inferred belongs_to select is named :country_id but Rails attaches its errors to :country.
-
#input(*modifiers, type: :text) ⇒ Object
--- leaf component builders (return component instances to render) --- Component classes resolve through the form's theme (see PhlexForms::Theme), so the same field renders daisy or plain.
- #invalid? ⇒ Boolean
- #label(text = nil, *modifiers, &block) ⇒ Object
- #radio(value, *modifiers, **options) ⇒ Object (also: #radio_button)
-
#required? ⇒ Boolean
Inferred from the model's presence validators (ActiveModel).
- #rich_textarea(*modifiers) ⇒ Object (also: #rich_text_area)
- #select(choices = nil, **options) ⇒ Object
- #textarea(*modifiers) ⇒ Object (also: #text_area)
- #toggle(*modifiers) ⇒ Object
-
#wrapped_input(*modifiers, type: :text) ⇒ Object
daisyui v5 "icon/text inside the field" wrapper.
Constructor Details
#initialize(name:, model:, scope:, errors:, form:, error_name: nil) ⇒ Field
error_name: where errors live when it differs from the input's name — an inferred belongs_to select is named :country_id but Rails attaches its errors to :country.
19 20 21 22 23 24 25 26 |
# File 'lib/forms/field.rb', line 19 def initialize(name:, model:, scope:, errors:, form:, error_name: nil) @name = name @error_name = error_name || name @model = model @scope = scope @errors = errors @form = form end |
Instance Attribute Details
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
14 15 16 |
# File 'lib/forms/field.rb', line 14 def errors @errors end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
14 15 16 |
# File 'lib/forms/field.rb', line 14 def model @model end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
14 15 16 |
# File 'lib/forms/field.rb', line 14 def name @name end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
14 15 16 |
# File 'lib/forms/field.rb', line 14 def scope @scope end |
Instance Method Details
#apply_validations(options) ⇒ Object
Merge a per-call validate: override into the field's option hash. Strips
:validate and, when rules apply, merges the Stimulus data into data:.
Falls back to the form-level introspector when :validate is absent.
validate: false → opt this field out
validate: true → the form-level introspector
validate: { length: {…} } → explicit inline rules
164 165 166 167 168 169 170 171 |
# File 'lib/forms/field.rb', line 164 def apply_validations() return consume_validate() if .key?(:validate) data = form_introspector.data_attributes_for(@name) return if data.empty? .merge(data: merge_data([:data], data)) end |
#checkbox(*modifiers) ⇒ Object Also known as: check_box
60 61 62 |
# File 'lib/forms/field.rb', line 60 def checkbox(*modifiers, **) theme[:checkbox].new(*modifiers, checked: field_value, **field_attributes.except(:value), **) end |
#choices_select(choices = nil, *modifiers, **options) ⇒ Object
Enhanced select: choices.js-backed when searchable, native otherwise.
84 85 86 87 88 89 90 91 92 |
# File 'lib/forms/field.rb', line 84 def choices_select(choices = nil, *modifiers, **) searchable = .delete(:searchable) { false } opts = () if searchable theme[:choices_select].new(*modifiers, choices:, selected: field_value, searchable: true, **opts) else theme[:select].new(*modifiers, choices:, selected: field_value, **opts) end end |
#control(label: nil, hint: nil, required: false) ⇒ Object
98 99 100 |
# File 'lib/forms/field.rb', line 98 def control(label: nil, hint: nil, required: false, **, &) theme[:control].new(label:, hint:, error: , for: field_id, required:, **, &) end |
#field_id ⇒ Object
135 136 137 138 139 140 141 |
# File 'lib/forms/field.rb', line 135 def field_id if @scope "#{@scope.tr('[', '_').delete(']')}_#{@name}" else @name.to_s end end |
#field_label ⇒ Object
Humanized label text: the model's human_attribute_name when available.
105 106 107 108 109 110 111 |
# File 'lib/forms/field.rb', line 105 def field_label if @model.respond_to?(:class) && @model.class.respond_to?(:human_attribute_name) @model.class.human_attribute_name(@name) else @name.to_s.tr("_", " ").capitalize end end |
#field_name ⇒ Object
131 132 133 |
# File 'lib/forms/field.rb', line 131 def field_name @scope ? "#{@scope}[#{@name}]" : @name.to_s end |
#field_value ⇒ Object
Ransack-safe getter: dispatches through method_missing consumers (e.g. Ransack::Search predicate getters) but only swallows the direct dispatch miss.
145 146 147 148 149 150 151 152 153 |
# File 'lib/forms/field.rb', line 145 def field_value return nil unless @model @model.public_send(@name) rescue NoMethodError => e raise unless e.receiver.equal?(@model) && e.name == @name nil end |
#file(*modifiers) ⇒ Object
56 57 58 |
# File 'lib/forms/field.rb', line 56 def file(*modifiers, **) theme[:file].new(*modifiers, **field_attributes.except(:value), **) end |
#hidden ⇒ Object
46 47 48 |
# File 'lib/forms/field.rb', line 46 def hidden(**) theme[:input].new(type: :hidden, **field_attributes, **) end |
#input(*modifiers, type: :text) ⇒ Object
--- leaf component builders (return component instances to render) --- Component classes resolve through the form's theme (see PhlexForms::Theme), so the same field renders daisy or plain.
32 33 34 |
# File 'lib/forms/field.rb', line 32 def input(*modifiers, type: :text, **) theme[:input].new(*modifiers, type:, **field_attributes, **) end |
#invalid? ⇒ Boolean
125 126 127 128 129 |
# File 'lib/forms/field.rb', line 125 def invalid? return false unless @errors @errors.include?(@name) || @errors.include?(@error_name) end |
#label(text = nil, *modifiers, &block) ⇒ Object
94 95 96 |
# File 'lib/forms/field.rb', line 94 def label(text = nil, *modifiers, **, &block) theme[:label].new(*modifiers, text: text || (block ? nil : field_label), for: field_id, **, &block) end |
#radio(value, *modifiers, **options) ⇒ Object Also known as:
69 70 71 72 73 74 75 76 |
# File 'lib/forms/field.rb', line 69 def radio(value, *modifiers, **) theme[:radio].new( *modifiers, value:, checked: field_value == value, **field_attributes.merge().merge(id: "#{field_id}_#{value}") ) end |
#required? ⇒ Boolean
Inferred from the model's presence validators (ActiveModel). False when the model doesn't expose validators.
115 116 117 118 119 120 121 122 123 |
# File 'lib/forms/field.rb', line 115 def required? return false unless @model.respond_to?(:class) && @model.class.respond_to?(:validators_on) @model.class.validators_on(@name).any? do |v| v.is_a?(ActiveModel::Validations::PresenceValidator) && !conditional?(v) end rescue StandardError false end |
#rich_textarea(*modifiers) ⇒ Object Also known as: rich_text_area
41 42 43 |
# File 'lib/forms/field.rb', line 41 def rich_textarea(*modifiers, **) theme[:rich_textarea].new(*modifiers, name: field_name, id: field_id, value: field_value, **) end |
#select(choices = nil, **options) ⇒ Object
79 80 81 |
# File 'lib/forms/field.rb', line 79 def select(choices = nil, **) theme[:select].new(choices:, selected: field_value, **()) end |
#textarea(*modifiers) ⇒ Object Also known as: text_area
36 37 38 |
# File 'lib/forms/field.rb', line 36 def textarea(*modifiers, **) theme[:textarea].new(*modifiers, **field_attributes, **) end |
#toggle(*modifiers) ⇒ Object
65 66 67 |
# File 'lib/forms/field.rb', line 65 def toggle(*modifiers, **) theme[:toggle].new(*modifiers, checked: field_value, **field_attributes.except(:value), **) end |
#wrapped_input(*modifiers, type: :text) ⇒ Object
daisyui v5 "icon/text inside the field" wrapper. The block renders the leading content (icon, prefix); the bare input is wired to this field.
52 53 54 |
# File 'lib/forms/field.rb', line 52 def wrapped_input(*modifiers, type: :text, **, &) theme[:wrapped_input].new(*modifiers, type:, **field_attributes.except(:error), error: invalid?, **, &) end |