A Little Form Help From Your Friends

I have a dirty little secret, and I’m not sure if it’s really the best idea to announce it here being the rather public forum that it is, but here goes:

I hate HTML.

You’re probably wondering why that little statement rates up there as a “dirty little secret,” involving, as it does, no dubious fluids, strange positions, or missing articles of clothing. But it is something pretty wrong for a web monkey to admit, it seems. I look around and watch people talk about how they just love XHTML and CSS and making their pages pass the vaunted XHTML Strict litmus test. But not I; nay, I view it as a necessary evil.

        <span id="more-3671"></span>
(Although CSS is a far sight better than the alternative, I wouldn’t say I love it. I wouldn’t even say I’m attracted to it in a more-than-platonic way.)

So it follows that one of my favorite parts of Rails–once you get past the happy shiny “full-stack framework,” and the whole “golly gee, I don’t have to write almost any SQL” rigamarole–is helpers. Especially form helpers. Less HTML == happier Amy.

People Helping Helpers Help People

First Rails cut out the repetitive cruft of building applications from the ground up on your own, or fighting with monolithic XML documents. Then it cut out all those pesky repetitive queries you’d otherwise have to write by hand. The only task left to be minimized, other than clients? HTML.

So in come the helpers. They really do help.

There are essentially two major types of helpers:

  • Helpers included with Rails
  • Helpers you write yourself

Right now we’re only concerned with the former, and a subgroup of the former to be even more exact–today, I’m writing about form helpers. As the name so cleverly implies, form helpers help you make forms. (Of all the HTML I love to hate, forms definitely occupy a very special place in my heart.)

So They Help. Great. What are they?

Helpers are methods (functions) that aren’t explicitly part of a controller class or model class.

In this case, they’re short little functions that spit out properly formed HTML and take into account all the nitty gritty details of form creation such as automatically repopulating when the form submission fails.

Where Do They Go?

Form helpers are to be used in in your application’s views, including layouts and partials. It’s that simple. Most helpers that come with Rails are meant to do something HTMLy, but it’s not actually required that they spit out HTML. You can write helpers to use in controllers, if you so choose.

When you create your own, they go in one of the files in RAILSROOT/app/helpers/. But that’s another article altogether.

How Do They Work?

The form helpers you’ll really be interested in work with data from your models. You’ll want to use form helpers to link to specific columns—aka methods—for the table you’ve linked to your model. So there’s a form helper that’s right for just about every column in your table that you want to be represented by a form field.

There are also form helpers for non-model data. You may find they’ll come in useful sometime, too.

Why?

The magic of Rails depends on forms being crafted in exactly the way it expects. It can be a pain to do it by hand—it’s just a lot cleaner and more elegant to go ahead and use the wonderful tools that the Rails makers, in their infinite wisdom, saw fit to provide us. And who can argue with faster, easier, and better?

But enough with vague, but effusive, plattitudes. What you really want to know is this:

  • Form helpers take the HTML load off your back
  • They result in cleaner views
  • They handle stuff like creating an invisible field with the opposite value for checkboxes, moving that particular data handling out of the business logic
  • They handle fun tricks like auto-filling themselves when an object of their type is available in the view
  • When you have validations in your model and are using form helpers, any field that doesn’t pass its validations is automatically surrounded in a div so that you can color it red or what-have-you to warn the user

What’s the Collective Noun for Form Helpers?

Helpings, of course. Form-related helpers come in the following flavors:

  • FormHelper. A collection of tags to create standard inputs like checkboxes, textareas, and so on.
  • FormOptionsHelper. For selects, including some helpers with pre-defined data like country names.
  • FormTagHelper. These are to save time when creating form elements that don’t follow the AR conventions (e.g., they’re not linked to a model), including starting & ending forms and buttons.

Gettin’ Your Feet Wet

You can get the general gist of how these things work: you have the form helper method, and it takes arguments. Typical arguments include the type of object you’re working on, the method inside the object that the form field applies to, and other options for the field like size.

The exact format varies among the kinds of form helpers, and of course somewhat for each individual form helper. In fact, the only constant you can count on is change itself! (OK, not really, but I always wanted to experience an angry mob with torches outside my home.)

Let’s take, for example, the checkbox_helper. If you go to its API docs, you’ll see something like this:

check_box(object, method, options = {}, checked_value = "1", unchecked_value = "0")

The first things it takes are the object and method attributes. Remember that these map to the name of the type of object you’re working on (e.g. what’s the model? Book? then you want to feed it book) and the name of the column in its database table. If you wanted a column on your Book and it would be best represented by a checkbox—say, “I own it or I don’t own it,” in a column named ownership—then you could do it this way:

check_box('book','ownership')

You could get fancier, and give the checkbox a CSS class, and set the values it should return if it’s checked or not checked:

check_box('book','ownership', "class='book_checkboxes'",'yes','no')

As you can see in the syntax definition, the defaults for checked_value and unchecked_value are 1 and 0, respectively. When you see a method attribute followed by an = sign, then you know that what is on the other side of the equals sign is the default. If you don’t set it, Rails will assume the default.

Using Form Options Helpers

The checkbox helper is a simple example; most of the plain old form helpers/form tag helpers are. But you may find that other helpers are a bit trickier. Take, for example, the group of form option helpers.

Here’s an example of collection_select, which takes a collection of AR objects and gives you a pop-up select menu (you can also use the HTML setting to turn it into a multi-select box):

<%= collection_select('book', 'rating_id', @ratings, 'id', 'name') %>

This is deconstructed thusly: First you have the object name, the book, which tells you what you’re working on. Then you have the method (or column) name, rating_id. After that, you have the collection to draw the options from (you’d get that with something like @ratings = Rating.find_all in your controller), the ratings column to use for the form key, and then the ratings column to use for the form’s apparent value (to the user).

Here’s the syntax for it, found in the API docs:

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

As you can see, it’s a little hectic. But you’ll get the hang of it.

Multiple Models, One Form

Form helpers will even work when you’re using a form to link together multiple models via associations. Just grab the additional collections in the appropriate method in your controller, like thus:

@book = Book.find(@params[:id]) @sections = Section.find_all @authors = Author.find_all

And then you’ll be free to create form helpers for both the tag and author models, like thus:

<%= text_field("book", "title", "size" => 35) %>
<%= collectionselect('book', 'sectionid', @sections, 'id', 'name') %>
<%= collection_select('author', 'id', @authors, 'id', 'firstName') %>

As you can see, since an article has only one section, the collection_select is called with the book object and the method name for the column section_id. Whereas there’s a hasandbelongs_to_many relationship between authors and books, so the collection_select uses the object name and column from the author model.

The Wrap-Up

I never was good at endings. Or goodbyes. So goodbye, gentle reader. I hope I served as a handy flashlight in the dark caverns of the API docs. You should download my form helpers cheat sheet. Enjoy!

And don’t hesitate to stop by the IRC channel, #rubyonrails on irc.freenode.net, and say hello. I’m eriberri and there are lots of helpful people about.

No Comments

  1. PJ Hyett says:

    I also enjoy using the helpers a lot, much to my surprise. The only problem I have with them is that if I were to use the HTML they generate on another non-Rails site, I have to execute the page first and then view the source. I wish that there was an easier way to preview the real HTML underneath. Perhaps there is and I’ve missed it? That would be a great feature for TextMate.

  2. marvin says:

    Sweet! 🙂 Would you mind splitting the long lines of code in the second last section into a few lines? It’s causing some ugly horizontal scrolling on my browser.

  3. Geoff says:

    A little nugget I learned recently: Rails fills the default values for items only when there is an instance variable of the same name (but not for local variables).

    You can assign an instance variable if you don’t already have one:

    bq. @homework = @assignments.homework

    bq. text_field "homework", "grade"

    Or, you can do it manually:

    bq. text_field "homework", "grade", :value =&gt; @assignments.homework.grade

  4. Amy says:

    Hi Marvin,

    Sorry! I checked for those but I missed that one. Fixing…

  5. Amy says:

    PJ, do you ever use FireFox? You should download the Aadvark plugin for it. Load up a page, turn it on from the menu, and then mouse over the section of the page whose source you want. Type the letter ‘v’ on your keyboard. A pop-up window will appear with the HTML properly formatted and colorized… it’s kickass. It doesn’t entirely solve the problem, but it makes getting source MUCH less hassle!

    Geoff, that’s a very good point. Thank you. 🙂

  6. Nalin says:

    How would I create a single-select field that jut had a Y/N option which would bind automatically using the Ruby helpers?

    So I don’t have an object that holds these two "Y" and "N" values but I don’t want to write my own HTML because then on an edit, I have to set the value accordingly manually. Can the helper take a map or hash object where they key and value can be used for the element value and label respectively?

  7. Nathan says:

    Something that I haven’t found a lot of tutorials on is the has_many like the following…

    User has many phone numbers. Phone Numbers has a name and number.

    I want to enable the user to create a user and add their many phone numbers all within a single form.

    The normal flow is to first create the user object then go to the add numbers page and add the numbers one by one. It seems like it should be possible to do this via a single form and single submit.

  8. April says:

    I’m running into an obnoxious, silly problem trying to write my very first little rails mini-app. My form doesn’t seem to want to associate itself with my data. I can print out the data, but when I use form helpers it doesn’t automatically fill in with it. I know using your tutorial doesn’t make you my automatic tech support, but I assure you that the final product will help ensure the existance of small fuzzy felt monsters (and the world needs that).

    In my model (link.rb), I have: def self.pending_review find(:all, :conditions => "status =2") end

    In my controller (admin_controller.rb) I have: def review @awaiting_review = Link.pending_review end

    In my view (review.rhtml) I have (annotated): <%= form_tag(:action => "review") %> <% for link in @awaiting_review %> <%= link.title %> <%= text_field(:link, :title) %> <% end %>

    Link title displays no problem, but the text field is just blank. I’m trying to display the title in it, so I can edit it and hit save.

    If you or one of your readers happens to know the answer I’d be grateful for any help.

  9. April says:

    Never mind! The comments loaded this time and the answer was right there.

  10. Thanks for the wonderful infomation and tutorial, I am looking forward to trying this at home later today.

    What a major bummer, It looks like you have some Trackback spammers on this post, first time I have seen that.

  11. But I’m not sure why!

  12. i am not sure as to why.

  13. Delhi India says:

    I’m working…

  14. I was very dissapointed of this 🙂

  15. Kristy Luke says:

    Hey There, Hello am writing to see if you can help me with my friend thats doesn’t want to leave Blenheim and she has two chooses one to move to Aussie woth her Dad or stay in New Zealand with her Mum, but my friend would love to stay here because she really loves our school and where her mum is going she doesn’t like that school.

    Many Thanks Kristy Luke

    P.S MY EMAIL ADDRESS IS kristyluke1@hotmail.com(all in small letters). Can you please give me advise for my dear friends.

  16. I was very dissapointed of this 🙁

  17. The content of your show is great, I really enjoy it 🙂

  18. That’s awesome 🙁

  19. Gonna have to give it a try…

  20. But I’m not sure why 🙂

  21. Catalogo Vm says:

    Well at last catched the problem 🙂

  22. Well done, nice instructions…

  23. Just thought I’d make a note about a problem 🙂

  24. The problem is my browser 🙂

  25. i am not sure as to why!

  26. Ente Locale says:

    Thanks for taking the time to do it…

  27. Hay Stacker says:

    Well at last catched the problem.

  28. Just thought I’d make a note about a problem.

  29. Thanks for taking the time to do it…

  30. Thanks. Updated appropriately!

  31. That is strange 🙂

  32. I’m working 🙂

  33. I use Firefox in Ubuntu…

  34. The problem is my browser 🙁

  35. Very nice write up.

  36. Well done, nice instructions!

Hey, why not get a shiny
Freckle Time Tracking
account?