Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ f.Checkbox(:terms) ; f.Toggle(:notify) ; f.FileInput(:avatar)
f.Label(:email) ; f.Hidden(:token) ; f.submit("Save", :primary)
```

Migrating from Rails' `FormBuilder`? `f.hidden_field(:token, value: x)` is a
snake_case alias for `f.Hidden` — same model-bound path, and an explicit
`value:` wins over the model's current value — so those call sites port over
verbatim.

For a bespoke widget (date picker, tag field, remote select), wrap it in
`f.Control` and bind through the public helpers — this is the supported path,
not a fork reason:
Expand Down
7 changes: 7 additions & 0 deletions lib/phlex_forms/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ def Hidden(name, **)
render field_object(name).hidden(**)
end

# Rails FormBuilder-compatible hidden field, for straight migration of
# `form.hidden_field(:token, value: x)` call sites. Same model-bound path as
# `Hidden`; an explicit `value:` wins over the model's current value.
def hidden_field(name, **)
render field_object(name).hidden(**)
end

def Label(name, text = nil, *modifiers, **, &)
render field_object(name).label(text, *modifiers, **, &)
end
Expand Down
35 changes: 35 additions & 0 deletions spec/forms/components_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,41 @@ def render_form_via(model, **args, &block)
end
end

describe "hidden_field (Rails FormBuilder migration aid)" do
it "renders a scoped hidden input with an explicit value: override" do
user = build_model(:user, name: "Ada")

output = render_form(user) do |f|
f.hidden_field(:accepted_terms_document_id, value: 42)
end

expect(output).to include('type="hidden"')
expect(output).to include('name="user[accepted_terms_document_id]"')
expect(output).to include('id="user_accepted_terms_document_id"')
expect(output).to include('value="42"')
end

it "binds the value from the model when none is passed" do
user = build_model(:user, token: "abc123")

output = render_form(user) { |f| f.hidden_field(:token) }

expect(output).to include('type="hidden"')
expect(output).to include('name="user[token]"')
expect(output).to include('value="abc123"')
end

it "renders under the plain theme too (bare hidden input)" do
user = build_model(:user, token: "abc123")

output = render_form(user, theme: :plain) { |f| f.hidden_field(:token) }

expect(output).to include('<input type="hidden"')
expect(output).to include('name="user[token]"')
expect(output).to include('value="abc123"')
end
end

describe "fields_for (has_many nested attributes)" do
it "renders indexed nested attribute names" do
child = Class.new do
Expand Down
Loading