Class: Forms::TagField

Inherits:
Phlex::HTML
  • Object
show all
Includes:
Phlex::Reactive::ClientBindings
Defined in:
lib/forms/tag_field.rb

Overview

A model-bound tag/chip input, composed from phlex-reactive's CLIENT-ONLY tag primitives (form state, no token, zero round trips). phlex-forms owns the polished chrome — daisyUI styling, model binding, the hidden comma-joined field — while phlex-reactive owns the behavior contract.

f.field :tags, as: :tags, suggestions: %w[Ruby Rails Hotwire Postgres]
f.field :tags, as: :tags, suggestions: { "Postgres" => "postgres database db sql" }

Submits ONE comma-joined param (user[tags] = "Ruby,Rails"), the primitive's wire contract — the model splits it (an attribute :tags + a tags= writer, or an ActiveModel array type).

This file is autoloaded ONLY when Phlex::Reactive is present (the Forms::Live gate in lib/phlex_forms.rb) because it includes ClientBindings at class level. The reactive_tags_* client helpers require phlex-reactive >= 0.11.4.

It uses the reactive_tags_add/option/remove helpers for the chip/query/option behavior, and the ROOT's wire attrs come from the 0.12.2 escape-hatch sugar: reactive_tags(name: @name) takes the per-instance wire name verbatim ("user") — the class-level reactive_scope compile can't express it — validated at render; reactive_filter(input: "#…_query") targets the query input by id so it never submits, and (unlike the old raw -input-only attr) also emits data-reactive-filter-option so the 0.12.x client type-ahead actually runs (issue #6 Caveats 1 & 2).

Direct Known Subclasses

Plain::TagField, RootlessTagField

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*modifiers, name:, id:, value: nil, suggestions: [], error: false, placeholder: "Add a tag…", **attributes) ⇒ TagField

Returns a new instance of TagField.



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/forms/tag_field.rb', line 31

def initialize(*modifiers, name:, id:, value: nil, suggestions: [], error: false,
               placeholder: "Add a tag…", **attributes)
  @modifiers = modifiers
  @name = name # "user[tags]" — instance-dynamic
  @id = id
  @value = value.is_a?(Array) ? value.compact.join(",") : value.to_s
  @suggestions = normalize_suggestions(suggestions)
  @error = error
  @placeholder = placeholder
  @attributes = attributes
  super()
end

Class Method Details

.query_id(id) ⇒ Object

The query input's id — the reactive_filter(input:) target and the id the search input itself carries. Public so Forms::Live can derive the same id when it hoists the tag wire attrs onto the

root (rootless widget).



56
# File 'lib/forms/tag_field.rb', line 56

def self.query_id(id) = "#{id}_query"

Instance Method Details

#view_templateObject



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

def view_template
  # Standalone: THIS div is the reactive root. Inside a Forms::Live form the
  # widget renders rootless (Forms::RootlessTagField) — the outer <form> is
  # the root and carries these tag attrs — so the form owns the hidden field.
  div(**mix(reactive_root(id: "#{@id}_widget"), root_tag_attributes, class: root_classes)) do
    body
  end
end