Class: Forms::Form
- Inherits:
-
Phlex::HTML
- Object
- Phlex::HTML
- Forms::Form
- Includes:
- Phlex::Rails::Helpers::FormAuthenticityToken, PhlexForms::Builder
- Defined in:
- lib/forms/form.rb
Direct Known Subclasses
Constant Summary
Constants included from PhlexForms::Builder
PhlexForms::Builder::INPUT_TYPE_INFERENCE, PhlexForms::Builder::INPUT_TYPE_MODIFIERS
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#method ⇒ Object
readonly
Returns the value of attribute method.
-
#model ⇒ Object
(also: #object)
readonly
Returns the value of attribute model.
-
#scope ⇒ Object
(also: #object_name)
readonly
Returns the value of attribute scope.
-
#theme ⇒ Object
readonly
Returns the value of attribute theme.
-
#url ⇒ Object
readonly
Returns the value of attribute url.
-
#validate ⇒ Object
readonly
Returns the value of attribute validate.
Instance Method Summary collapse
-
#collection_check_boxes(name, collection, value_method, text_method) ⇒ Object
Rails-style collection_check_boxes.
-
#collection_select(name, collection, value_method, text_method, options = {}, html_options = {}) ⇒ Object
Rails-style collection_select over an enumerable of records.
-
#default_field_variants ⇒ Object
Default variants prepended to every
field's inner input: global config first, then this form's field_variants: (call-site modifiers stack last). - #field_id(name) ⇒ Object
-
#field_name(name) ⇒ Object
Public name/id/value helpers for external components mirroring the Rails API.
-
#field_object(name, error_name: nil) ⇒ Object
(also: #[])
Return a Forms::Field for a name (used by the Builder mixin).
- #field_value(name) ⇒ Object
-
#fields_for(association_name, model = nil, nested_attributes: true) ⇒ Object
Nested attributes.
-
#initialize(*modifiers, model: nil, scope: nil, url: nil, method: nil, validate: false, field_variants: nil, theme: nil, live: nil, **options) ⇒ Form
constructor
A new instance of Form.
- #rich_textarea(name, *modifiers) ⇒ Object (also: #rich_text_area)
- #submit ⇒ Object
- #time_zone_select(name, *modifiers, selected: nil) ⇒ Object
-
#validations_introspector ⇒ Object
Introspector for the bound model when client-side validation is enabled, or a no-op Null otherwise.
- #view_template ⇒ Object
Methods included from PhlexForms::Builder
#Checkbox, #Control, #FileInput, #Hidden, #Input, #Label, #Radio, #Select, #Textarea, #Toggle, #field, #group, #row
Constructor Details
#initialize(*modifiers, model: nil, scope: nil, url: nil, method: nil, validate: false, field_variants: nil, theme: nil, live: nil, **options) ⇒ Form
Returns a new instance of Form.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/forms/form.rb', line 28 def initialize(*modifiers, model: nil, scope: nil, url: nil, method: nil, validate: false, field_variants: nil, theme: nil, live: nil, **) if live raise ArgumentError, "live validation requires a Forms::Base subclass — the endpoint rebuilds the " \ "form from its class, and an inline block cannot be serialized. Declare " \ "`live model: #{record_from(model)&.class&.name || 'YourModel'}` on the class instead." end super() @base_modifiers = modifiers @field_variants = Array(field_variants) @theme = PhlexForms::Theme.resolve(theme) @options = @model = record_from(model) # scope: false opts out of scoping entirely — bare field names for # reactive row editors / <template>-cloned rows. nil means "derive". @scope = scope == false ? nil : (scope&.to_s || derive_scope(@model)) @url = url || derive_url(model) @method = method || derive_method(@model) @errors = (@model.errors if @model.respond_to?(:errors)) @validate = validate end |
Instance Attribute Details
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def errors @errors end |
#method ⇒ Object (readonly)
Returns the value of attribute method.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def method @method end |
#model ⇒ Object (readonly) Also known as: object
Returns the value of attribute model.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def model @model end |
#scope ⇒ Object (readonly) Also known as: object_name
Returns the value of attribute scope.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def scope @scope end |
#theme ⇒ Object (readonly)
Returns the value of attribute theme.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def theme @theme end |
#url ⇒ Object (readonly)
Returns the value of attribute url.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def url @url end |
#validate ⇒ Object (readonly)
Returns the value of attribute validate.
20 21 22 |
# File 'lib/forms/form.rb', line 20 def validate @validate end |
Instance Method Details
#collection_check_boxes(name, collection, value_method, text_method) ⇒ Object
Rails-style collection_check_boxes. Emits a hidden field so an empty selection still submits, then yields a builder per collection item.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/forms/form.rb', line 118 def collection_check_boxes(name, collection, value_method, text_method, &) return unless block_given? input(type: "hidden", name: "#{field_name(name)}[]", value: "") current = Array(@model&.public_send(name)) current_ids = current.map { |v| v.respond_to?(value_method) ? v.public_send(value_method) : v } collection.each do |item| item_value = item.public_send(value_method) item_text = text_method.is_a?(Proc) ? text_method.call(item) : item.public_send(text_method) yield Forms::CollectionCheckBoxBuilder.new( object: item, value: item_value, text: item_text, checked: current_ids.include?(item_value), name: "#{field_name(name)}[]", id: "#{field_id(name)}_#{item_value}" ) end end |
#collection_select(name, collection, value_method, text_method, options = {}, html_options = {}) ⇒ Object
Rails-style collection_select over an enumerable of records.
137 138 139 140 141 142 143 144 |
# File 'lib/forms/form.rb', line 137 def collection_select(name, collection, value_method, text_method, = {}, = {}) choices = collection.map do |item| text = text_method.is_a?(Proc) ? text_method.call(item) : item.public_send(text_method) [text, item.public_send(value_method)] end choices = [[[:prompt], ""]] + choices if [:prompt] render field_object(name).select(choices, **.except(:prompt), **) end |
#default_field_variants ⇒ Object
Default variants prepended to every field's inner input: global config
first, then this form's field_variants: (call-site modifiers stack last).
148 149 150 |
# File 'lib/forms/form.rb', line 148 def default_field_variants PhlexForms.config.field_variants + @field_variants end |
#field_id(name) ⇒ Object
154 |
# File 'lib/forms/form.rb', line 154 def field_id(name) = @scope ? "#{@scope}_#{name}" : name.to_s |
#field_name(name) ⇒ Object
Public name/id/value helpers for external components mirroring the Rails API.
153 |
# File 'lib/forms/form.rb', line 153 def field_name(name) = @scope ? "#{@scope}[#{name}]" : name.to_s |
#field_object(name, error_name: nil) ⇒ Object Also known as: []
Return a Forms::Field for a name (used by the Builder mixin).
72 73 74 |
# File 'lib/forms/form.rb', line 72 def field_object(name, error_name: nil) Forms::Field.new(name:, model: @model, scope: @scope, errors: @errors, form: self, error_name:) end |
#field_value(name) ⇒ Object
155 |
# File 'lib/forms/form.rb', line 155 def field_value(name) = field_object(name).field_value |
#fields_for(association_name, model = nil, nested_attributes: true) ⇒ Object
Nested attributes. Yields a FieldsForBuilder per association (single) or per
item (has_many), with the correctly-indexed nested scope.
nested_attributes: false nests under the raw name (no _attributes suffix)
for JSONB/hash columns that aren't Rails nested attributes.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/forms/form.rb', line 100 def fields_for(association_name, model = nil, nested_attributes: true, &) return unless block_given? associated = model || (@model.public_send(association_name) if @model.respond_to?(association_name)) attributes_key = nested_attributes ? "#{association_name}_attributes" : association_name.to_s base_scope = @scope ? "#{@scope}[#{attributes_key}]" : attributes_key if associated.respond_to?(:each_with_index) associated.each_with_index do |item, index| yield build_fields_for("#{base_scope}[#{index}]", item) end else yield build_fields_for(base_scope, associated) end end |
#rich_textarea(name, *modifiers) ⇒ Object Also known as: rich_text_area
81 82 83 |
# File 'lib/forms/form.rb', line 81 def rich_textarea(name, *modifiers, **) render field_object(name).rich_textarea(*modifiers, **) end |
#submit ⇒ Object
77 78 79 |
# File 'lib/forms/form.rb', line 77 def submit(*, **, &) render theme[:submit].new(*, model: @model, **, &) end |
#time_zone_select(name, *modifiers, selected: nil) ⇒ Object
86 87 88 89 90 91 92 93 94 |
# File 'lib/forms/form.rb', line 86 def time_zone_select(name, *modifiers, selected: nil, **) render Forms::TimeZoneSelect.new( *modifiers, name: field_name(name), id: field_id(name), selected: selected || @model&.public_send(name), ** ) end |
#validations_introspector ⇒ Object
Introspector for the bound model when client-side validation is enabled, or a no-op Null otherwise. Callers can always call #data_attributes_for(attr).
54 55 56 57 58 59 60 61 |
# File 'lib/forms/form.rb', line 54 def validations_introspector @validations_introspector ||= if @validate Forms::Validations::Introspector.for(@model) else Forms::Validations::Introspector::Null.new end end |
#view_template ⇒ Object
63 64 65 66 67 68 69 |
# File 'lib/forms/form.rb', line 63 def view_template(&) form(action: @url, accept_charset: "UTF-8", method: form_method, **form_attributes) do authenticity_token_field unless @method&.to_sym == :get method_field if @method && %i[get post].exclude?(@method.to_sym) yield self if block_given? end end |