Jan 27 2010
Using form_tag in Ruby on Rails
I had some trouble finding a less than simplistic example of using the form_tag in Rails yesterday and, after griping on twitter about it was called out and told to do it myself. Good advice. Here we go.
In Rails you’ve got two ways to create a form: form_for and form_tag. You see examples for form_for everywhere, because that’s what you use to directly interact with the models you created in your database. But a lot of times you need to create a form that has no interaction with your database and that isn’t mapped to any data model. Enter form_tag.
There are two syntatic differences between forms created with form_for and those with form_tag. First, you typically declare form_for like this:
<% form_for(@person) do |f| %>
And from then on you add fields using the f variable you just defined, like f.text_field or f.select. The other difference is that the way you declare the fields themselves changes. text_field becomes text_field_tag, select becomes select_tag and so on. The reason is that text_field is meant, just like form_for, to interact with a defined object model. All those ones ending in _tag are for unbound forms.
<% form_tag(create_ticket_path, :method=>'post', :multipart => true) do %>
In the example, you see a couple of things. First, when defining a form_tag you can’t just pass it an object and let rails figure out what needs to happen. You need to tell it, first, what URL to point at and, second, what HTTP call to make (typically post or put). Those are the first two parameters. In my case, I have a path defined in my routes.rb for create_ticket, so I can call create_ticket_path to get it. And I’m creating a support ticket, so I’m going to use the ‘post’ method.
The third parameter is where you pass your options, which are roughly the same as the options you can pass form_for. You’ll see in mine I passed it :multipart => true. I do that to define a multipart form so that I can let people upload a file on submission.
The other thing to note is when defining the input fields. When using form_for, you can just pass, say, text_field the symbol for the field you want it bound to. So, if you were creating a field for the subject of a support ticket request, you’d do:
<%= f.text_field :subject %>
Rails figures out the rest. Without the model, we need to do it ourselves and define the field’s id. (If you wanted to have a default value, you’d put it in the second parameter, but for this example it doesn’t really make sense).
<%= text_field_tag "subject" %>
Pretty simple, I know. But sometimes you just need to see how the syntax works, and it felt like every tutorial assumed every form would either use Rails’ models and form_for, or your form_tag usage would be hilariously simple. The full example of the form is below. Just like when you use form_for, if you’re catching this with a rails controller, everything will be available through the params[] array. So, to catch that text_field_tag “subject” all you have to do in your controller is grab for params[:subject] .
File uploads are their own story, but to get you started, it’ll be in params just like everything else. Just be sure to use :multipart => true or your form will quietly ignore that an attachment was chosen.
Easy, right?
<% form_tag(create_ticket_path, :method=>'post', :multipart => true) do %>
<p>
Subject:<br />
<%= text_field_tag "subject" %>
</p>
<p>
Priority: <br />
<%= select_tag("priority", options_for_select([['Critical', '1'],
['Important', '2'],['Standard','3']], '3')) %>
</p>
<p>
Describe your problem:<br />
<%= text_area_tag "description", "", :size=>"50x20" %>
</p>
<p>
Add an attachment:<br />
<%= file_field_tag "attachment" %>
</p>
<p>
<%= submit_tag 'Submit' %>
</p>
<% end %>
Great article :)