Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Rails3 equivalent of start_form_tag?

Newbie here...I am just going through, what is clearly, an old rails tutorial.

To create a form they have the following code:

<%= start_form_tag(:action => 'create')%>

        <%= text_field(:album, :title) %><br />
        <%= text_field(:album, :artist) %><br />
        <%= text_field(:album, :genre) %><br />
        <%= datetime_select(:album, :release_date) %><br />

        <%= submit_tag("Create") %>

    <%= end_form_tag %> 

What is the proper Rails3 syntax for this code?

Thanks.

like image 374
marcamillion Avatar asked Oct 12 '10 23:10

marcamillion


1 Answers

Quite a lot has changed since Rails 1 and I'm really impressed by the people who are willing to spend the time to sit down with Rails 3 and learn the differences (1). The community needs to be welcoming to these and the RTFM'ers aren't being helpful. This is where I think I can help out.

I would write that like this in Rails 3:

<%= form_for(@album) do |f| %>
  <p>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </p>

  <p>
    <%= f.label :artist %>
    <%= f.text_field :artist %>
  </p>

  <p>
    <%= f.label :genre %>
    <%= f.text_field :genre %>
  </p>

  <p>
    <%= f.label :release_date %>
    <%= f.datetime_select :release_date %>
  </p>

  <%= f.submit %>
<% end %>

Woah, so many changes! Where do we begin? The top line, of course!

form_for is a method that's been in Rails for a while and has become exceedingly useful over the past couple of major revisions. This method will take the @album variable set up in the controller and inspect it to determine a couple of things 1) where the form should go 2) what the values of the fields should be.

I should mention at this stage that it should be mentioned that your app/views/albums/new.html.erb should now look like this:

<h2>New Album</h2>
<%= render "form" %>

OMG, more new stuff! It's ok: render "form" will render the app/views/albums/_form.html.erb partial. This is where the above form_for tomfoolery should live now. Why? Let me continue explaining.

In your AlbumsController's new action, you'd have something like this:

def new
  @album = Album.new
end

form_for is smart enough to go: "hey, that's a new Album resource, he probably wants to go to the create action for this form because he's being a good lad and following the Rails conventions", so that's precisely what it'll do.

Except:

This will raise an exception that there's no albums_path method defined. What the?

In Ruby on Rails 2, RESTful routing became a Big Thing. It's best if you read the routing guide, since if I explained it I would only be repeating a lot of it. Basically: RESTful routing is a convention for the URL routes of your application that determines that when we do a POST (it's a form, remember?) to /albums, this will go to the create action.

But how does Rails know to do this?

Because in your config/routes.rb file you've put this line:

resources :albums

This defines routes to "The Seven" default actions of your controller (index, show, new, create, edit, update and destroy). Seriously, read the routing guide to know all about this beauty.

So back to our little form_for. It knows to go to /albums for a new object, which is cool and all. So what's up with the f block argument? Well, this is a form builder object which allows us to build form elements for the specific Album object from our new (and soon, edit) actions. When we call:

<%= f.text_field :title %>

We are doing the Rails 1 equivalent of:

<%= text_field :album, :title %>

It's just a little bit of syntatic sugar that Rails gives you in order to make your views more DRY.

I also added labels to your form because users need to know what the fields are that they're filling in. The beautiful part here is that the user won't see :title, but will see Title instead. For :release_date, they'll see "Release Date". Beautiful.

At the end of the form, I use f.submit. This will generate a submit button that says either "Create Album" if @album is a new object, or "Update Album" if it's a pre-existing object. But where do the pre-existing objects get edited?

In the edit action!

def edit
  @album = Album.find(params[:id]
end

In your app/views/albums/edit.html.erb file you'd have much the same you have in its new.html.erb sibling:

<h2>Editing Album</h2>
<%= render "form" %>

You can use precisely the same line in both of these to render the same partial, but it will perform differently if the object is new or if it is pre-existing (commonly referred to as persisted).

In the case of the edit action rendering this partial, it will generate a route to /albums/1 for the form and do a Rails-specialty PUT request to this resource. Another thing the routing guide will explain very well, I hope.

I'm sorry for the length of this answer but there's no real short answer for this question, it's quite a large change but trust me when I say it really is for the better.

Rails 3 rocks.

(1) Less so with the people who aren't.

like image 102
Ryan Bigg Avatar answered Oct 05 '22 23:10

Ryan Bigg