Forms in Ruby on Rails - Part 1
July 8th, 2007
I was confused for a pretty long time about how to create forms in Ruby on Rails. Since many (most?) Rails applications are tied to databases, it makes sense that many interactions with model data will be done via HTML forms. My problem though, was that there just seemed to be too many choices, and the choices are poorly named.
Tutorial after the jump…
There are four Rails methods to use when we create forms. We can chose from the following:
- form_for
- remote_form_for
- form_tag
- form_remote_tag
To my mind, none of these methods really indicate what they do or how to use them. The API documentation doesn’t really help either – at least not in really knowing how to use these methods. It makes sense that the remote methods deal with AJAX but I still can’t tell by looking what they do.
So now let’s go over the basics of each method and how and when to use them. We’re only going to cover the basics since you can get pretty fancy with each. Hopefully, though, if you can understand this article, you can use that as a springboard into figuring out the more advanced stuff.
Here’s the most important morcel of this tutorial. form_for and remote_form_for are both methods to use when you want to present model based data. form_tag and form_remote_tag are more general purpose methods which are not based on a model’s data.
form_for allows you to create forms based on the models you’ve created for your Rails app. Let’s say that we have a model named ‘Visitor’ tied to a database table, visitors, which contains a field named ‘avatar_name’. Our controller method that we’ll use to test form_for can look like this:
def form_for_test
@visitor = Visitor.find_first
end
So here we’re just grabbing the first row in the visitors table and putting it into the @visitor variable which will be accessible in the form_for_test.rhtml view. Here’s the view:
<html>
<head>
<title>Form_For Tester</title>
</head>
<body>
<% form_for :visitor, :url => { :action => 'save', :id => @visitor } do |f| %>
Avatar Name: <%= f.text_field :avatar_name %>
<%= submit_tag 'Save' %>
<% end %>
</body>
</hmtl>
This is a really trivial form, but I think it illustrates the basic concept. form_for’s first variable is the let’s the form know that it will be using the variable @visitor that we created up in the controller. The :url hash tells us which method on the controller we’ll execute when the submit button is pushed, and the :id parameter allows the controller to pull out the id of the visitor model from the params collection which we’ll see in a moment.
form_for’s contents reside within a ruby block which we see in the ‘do |f|’ bit. Since we’re in the block, we can make calls against the ‘f’ variable, which is defined as a ‘form_builder’ object. This gives us access to methods to define every sort of form element. In this simple example, we’re putting the avatar_name column value into a text_field which maps to an input control. You can see the API document for every other type of control here.
Here’s the ‘save’ method which gets called when we submit the form:
def save
visitor = Visitor.find(params[:id])
visitor.update_attributes(params[:visitor])
redirect_to :action => 'form_for_test'
end
This is a really trivial and simple implementation, but it gets the job done. First we find the visitor we’re updating from the database. We do this by grabbing the :id parameter (which we allowed for with ’:id => @visitor’ back in the view). Next we use some Rails magic. update_attributes grabs the visitor parameter representing the changes we made when we submitted the form and applies them to the model object and saves it to the database. In any rails app beyond a simple learning tool we’d use the fact that update_attributes returns false on failure to handle any errors.
Finally in our simple app we just redirect back to the page we came from. I’d probably rather go to a success page or give the user some indication that the save was successful. We’ll do that now with remote_form_for which allows AJAXy form posting.
Our controller method for our remote_form_for test page looks familiar:
def remote_form_for_test
@visitor = Visitor.find_first
end
Just the same as the form_for controller method.
The view, however, looks a little different:
<html>
<head>
<title>Remote_Form_For Tester</title>
<%= javascript_include_tag :defaults %>
</head>
<body>
<% remote_form_for :visitor, @visitor,
:url => { :action => 'save_remote', :id => @visitor },
:update => 'result',
:complete => visual_effect(:highlight, 'result') do |f| %>
Avatar Name: <%= f.text_field :avatar_name %>
<%= submit_tag 'Save' %>
<% end %>
<div id='result'>
</div>
</body>
</hmtl>
remote_form_for works exactly like form_for except we add in support for AJAX. So, we need to add the javascript_include_tag method into our head element. Also, note the additional :update and :complete parameters. :update indicates that after we post the form, we are going to do something with the ‘result’ div. :complete says that after the form has been successfully posted we’re going to do a scriptaculous highlight effect on whatever is in the ‘results’ div.
The possible AJAX parameters we can pass to remote_form_for are explained in the API documentation for the link_to_remote method which you can find here. You can find documentation for using scriptaculous effects with visual_effect here.
When the form posts we’re going to asynchronously call the ‘save_remote’ method in the controller. Here’s what it looks like:
def save_remote
visitor = Visitor.find(params[:id])
visitor.update_attributes(params[:visitor])
@result = 'Saved!'
render :partial => 'form'
end
The first two lines of this method are the same as the form_for save method we discussed earlier. We’ve added in a @result variable and given it a message about our assumed success. Then we render the _form.rhtml partial which will fit into the ‘results’ div as per the :update parameter in the remote_form_for method back in the view.
The _form.rhtml partial is stupidly simple:
<%= @result %>
So, we’re just putting ‘Saved!’ at the bottom of the form after the form is posted. Since this is done in an AJAX manner, the browser doesn’t load a new page, it just displays the result partial and then immediately performs a scriptaculous highlight effect which turns the div bright yellow and then slowly fades it back to normal.
We could also add a spinning graphic if the process took a while to let users know something is going on. We could have the results div slide down or fade in or perform all sorts of other effects.
That’s it for a quick run-down on form_for and remote_form_for. I’m still not really sure why those names were chosen. model_form might have made more sense. In the next couple of days I’ll conclude this series with a discussion of the more versatile yet even more confusingly named form_tag and form_remote_tag methods.
As usual, if I’ve made a mistake or if you know a better way to do something, please leave a comment. Similarly, if you’re having a problem with forms in Ruby on Rails, please leave a comment and ask. I’m no expert, but I’ll do my best to help. Thanks for reading!
July 9th, 2007 at 06:39 AM
Nice to see you’re back ! I hope you keep helping the masses. CU
July 9th, 2007 at 07:34 AM
I’m thinking the names were chosen to read well. form_tag and form_remote_tag (which should definitely be called remote_form_tag; dunno what they were thinking there) generate a tag for you, nothing more. form_for generates a form for a given object. form_for :my_object reads very nicely. It’s creating a form for my_object.
Also, find_first is deprecated since sometime-or-another. The preferred usage is find(:first) (ditto with find_all vs find(:all)).
Nice post :)
July 9th, 2007 at 09:27 AM
Thanks Shadowfiend! I was unaware that those methods had been deprecated. :) I totally agree that form_remote_tag seems really oddly named, and after learning what form_for does, the name makes a bit more sense, but going in blind the first time none of the method names made much sense to me.
July 11th, 2007 at 07:40 AM
Thanks for your article. It is well written and focused. I’m looking forward to your next article as I have had many problems in the past with form_tag and form_remote_tag.
Keep in mind, you could make aliases to those form methods which don’t make sense and rename them into something that makes sense to you. However, it won’t follow standard convention.
July 14th, 2007 at 04:17 AM
Thanks for your article, THE BRAZIL MAN. !!!
July 16th, 2007 at 05:11 PM
Hello, Tad.
Recently i published a rails website in a linux server. But i’m having problems with the rendering of forms. They just dont appear. Connection with the DB is ok, tag names are ok. Locally it works pretty well (in Windows, tough). Do you have any idea what can be the problem? I’m using rails 1.2.3 and ruby 1.8.6, local. I only found late that in the linux server they run rails 1.1.6 and rubt 1.8.4. But i took much time tracking diferences in the documentation and seems there is no diference in this matter.
Thanks in advance.
July 17th, 2007 at 08:07 AM
Hi Fabrico,
I think there are some changes from previous version of Rails to the newest as far as forms are concerned. Previously, the form methods did not have to be wrapped around a ruby block. Leave off the “do” at the end of the method and instead of “end” try “end_form_tag.” I’m not sure of the syntax, but it’s something like that.
My advice is to create a stupidly simple prototype and freeze the rails gems for rails 1.1.6. Use this command:
rake rails:freeze:edge TAG=rel_1-1-6
See this page.
This will copy that version of Rails into your application’s vendor directory. Then create the simplest possible form and test it in your environment which will now be running rails 1.1.6
Here’s a link to where you can find Documentation for 1.1.6 in a .chm file which you can open in Windows.
Give that a shot and let me know how it goes!
August 30th, 2007 at 08:30 PM
Thanks! You’ve totally saved my life!
September 18th, 2007 at 02:19 AM
Hi
Very interesting information! Thanks!
Bye
October 10th, 2007 at 05:30 PM
Finally something well explained and easy to understand. Keep up the good work!
December 13th, 2007 at 01:27 AM
Hi guys, all the best to you! Please tell me how to behave in such state – I need a very good credit ‘cause there’s a job I want so. But they won’t accept me, I’m a student. I’m going to improve credit and apply for secured credit cards. So far I visited a web site but I cannot rely on it until you tell me if it’s worth applying there! Please recommend
balance transfers and your credit score
January 8th, 2008 at 02:08 AM
Thanks for your great work! I appreciate it!
January 15th, 2008 at 02:49 PM
Thanks for the great article! By using ruby naming conventions, could you have named the html page that housed the form ‘save’, left the url:action: definition out of the form_for call and relied on postback to avoid having the redirect_to call in the controller?
January 19th, 2008 at 08:51 AM
Very good tutorial.
Will be using it on my site
January 27th, 2008 at 06:28 PM
Excellent. It would be even better for beginnger if you show the actual screen display. Thanks, RLUU
February 4th, 2008 at 10:26 PM
Please! Keep writting rubynoob… would you?
February 25th, 2008 at 09:58 AM
Thanks for the nice read, keep up the interesting posts. Good luck.
February 26th, 2008 at 02:44 AM
Hi Nice articles. Read both the parts.
I have a little query. Please tell me how to validate a form_tag used form values .
Since the form has no model associated, how and where can I check the for validations, mandatory fields and also for format.
thanks!!
March 10th, 2008 at 12:53 AM
Hi I am uploading an file and i want that that file is uploaded with id of that file so i use FileUtils.mkdir_p ’/public/amit/’ but it will create a number of files so help me Thanks Amit Agarwal
April 11th, 2008 at 07:44 PM
you should write an entry about setting up “acts_as_taggable” and do some quick examples :D
June 21st, 2008 at 06:15 PM
Nice Site! http://google.com
June 25th, 2008 at 02:02 PM
Thanks for the article! I’ve been searching through the web for awhile now, and I believe this is the only artice in existence that does a good job of explaining forms for Rails.