Class: Forms::Form

Inherits:
Phlex::HTML
  • Object
show all
Includes:
Phlex::Rails::Helpers::FormAuthenticityToken, PhlexForms::Builder
Defined in:
lib/forms/form.rb

Constant Summary

Constants included from PhlexForms::Builder

PhlexForms::Builder::INPUT_TYPE_INFERENCE, PhlexForms::Builder::INPUT_TYPE_MODIFIERS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PhlexForms::Builder

#Checkbox, #Control, #FileInput, #Hidden, #Input, #Label, #Radio, #Select, #Textarea, #Toggle, #field

Constructor Details

#initialize(*modifiers, model: nil, scope: nil, url: nil, method: nil, validate: false, **options) ⇒ Form

Returns a new instance of Form.



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/forms/form.rb', line 22

def initialize(*modifiers, model: nil, scope: nil, url: nil, method: nil, validate: false, **options)
  super()
  @base_modifiers = modifiers
  @options = options
  @model = record_from(model)
  @scope = 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

#errorsObject (readonly)

Returns the value of attribute errors.



20
21
22
# File 'lib/forms/form.rb', line 20

def errors
  @errors
end

#methodObject (readonly)

Returns the value of attribute method.



20
21
22
# File 'lib/forms/form.rb', line 20

def method
  @method
end

#modelObject (readonly)

Returns the value of attribute model.



20
21
22
# File 'lib/forms/form.rb', line 20

def model
  @model
end

#scopeObject (readonly)

Returns the value of attribute scope.



20
21
22
# File 'lib/forms/form.rb', line 20

def scope
  @scope
end

#urlObject (readonly)

Returns the value of attribute url.



20
21
22
# File 'lib/forms/form.rb', line 20

def url
  @url
end

#validateObject (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.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/forms/form.rb', line 97

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.



116
117
118
119
120
121
122
123
# File 'lib/forms/form.rb', line 116

def collection_select(name, collection, value_method, text_method, options = {}, html_options = {})
  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 = [[options[:prompt], ""]] + choices if options[:prompt]
  render field_object(name).select(choices, **options.except(:prompt), **html_options)
end

#field_id(name) ⇒ Object



127
# File 'lib/forms/form.rb', line 127

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.



126
# File 'lib/forms/form.rb', line 126

def field_name(name) = @scope ? "#{@scope}[#{name}]" : name.to_s

#field_object(name) ⇒ Object

Return a Forms::Field for a name (used by the Builder mixin).



54
55
56
# File 'lib/forms/form.rb', line 54

def field_object(name)
  Forms::Field.new(name:, model: @model, scope: @scope, errors: @errors, form: self)
end

#fields_for(association_name, model = nil) ⇒ Object

Nested attributes. Yields a FieldsForBuilder per association (single) or per item (has_many), with the correctly-indexed nested scope.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/forms/form.rb', line 79

def fields_for(association_name, model = nil, &)
  return unless block_given?

  associated = model || (@model.public_send(association_name) if @model.respond_to?(association_name))
  attributes_key = "#{association_name}_attributes"
  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



62
63
64
# File 'lib/forms/form.rb', line 62

def rich_textarea(name, *modifiers, **)
  render field_object(name).rich_textarea(*modifiers, **)
end

#submitObject



58
59
60
# File 'lib/forms/form.rb', line 58

def submit(*, **, &)
  render Forms::Submit.new(*, model: @model, **, &)
end

#time_zone_select(name, *modifiers, selected: nil) ⇒ Object



67
68
69
70
71
72
73
74
75
# File 'lib/forms/form.rb', line 67

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_introspectorObject

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).



36
37
38
39
40
41
42
43
# File 'lib/forms/form.rb', line 36

def validations_introspector
  @validations_introspector ||=
    if @validate
      Forms::Validations::Introspector.for(@model)
    else
      Forms::Validations::Introspector::Null.new
    end
end

#view_templateObject



45
46
47
48
49
50
51
# File 'lib/forms/form.rb', line 45

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