Written October 15, 2008. Tagged Ruby.
Say you have a two-column layout, where the layout looks something like:
<div id="left-column">
<%= yield %>
</div>
<div id="right-column">
<%= yield :sidebar %>
</div>
So your main content goes in the left column, and you can optionally specify content to go in the sidebar.
In a template using this layout, you might have a form where some parts are in the left column, some in the right:
<%# This goes in the left column: %>
<% form_for(@user) do |form| %>
<%= form.text_field :name %>
<%= form.submit %>
<% end %>
<%# This goes in the right column: %>
<% content_for(:sidebar) do %>
<%# These do not work %>
<%= form.radio_button :gender, "M" %>
<%= form.radio_button :gender, "F" %>
<% end %>
How are you to get the radio buttons inside the form? You can't just extend the form_for
block to surround the content_for
block; the form start and end tags would still be inside the left column and not encompass the right column.
This is what I would do. First, allow content to be injected into the layout before and after both columns:
<%= yield :before_content %>
<div id="left-column">
<%= yield %>
</div>
<div id="right-column">
<%= yield :sidebar %>
</div>
<%= yield :after_content %>
Then in the template we need to insert the start form tag before and the end form tag after:
<% content_for(:before_content) do %>
<%= form_tag(@post) %>
<% end %>
<% content_for(:after_content) do %>
</form>
<% end %>
Note that I'm using form_tag
where I used form_for
before. It seems form_for
can't be used without a block (=without creating an end tag as well).
form_tag
will transform a record into a RESTful route just like form_for
, but unlike that helper, it will not assign an id or class to the form, so be advised.
Now to the form fields. We're not using form_for
anymore, and even if we could have, the columns would not have been contained in its block. One solution is to use text_field_tag
and friends, manually specifying the field name and default value. But thankfully, we can use fields_for
.
fields_for
is basically like form_for
but without the form start and end tags. It's most commonly used to mix multiple models in the same form. We can use it like this:
<% content_for(:before_content) do %>
<%= form_tag(@post) %>
<% end %>
<% content_for(:after_content) do %>
</form>
<% end %>
<%# This goes in the left column: %>
<% fields_for(@user) do |form| %>
<%= form.text_field :name %>
<%= form.submit %>
<% end %>
<%# This goes in the right column: %>
<% content_for(:sidebar) do %>
<% fields_for(@user) do |form| %>
<%= form.radio_button :gender, "M" %>
<%= form.radio_button :gender, "F" %>
<% end %>
<% end %>
This will set proper field names ("user[name]", "user[gender]") and default values (if @user
has a name or gender already).
One thing to do note is that if you have other forms in the right column already (perhaps a search box), that form will of course be nested in this one and weirdness may ensue. In that case, rethink your layout.