Skip to content

Instantly share code, notes, and snippets.

@clamstew
Last active August 29, 2015 13:57
Show Gist options
  • Save clamstew/9350138 to your computer and use it in GitHub Desktop.
Save clamstew/9350138 to your computer and use it in GitHub Desktop.
rough version of the form for lesson

form_for 'helper' method

Up until now we have looked at the create and update controller action in Rails in the context of using a basic <html> form tag, <form></form>.

When you wanted to POST a new movie to your Metube app:

'new' action

First you made a new controller-action in your videos controller. This action was mapped to the GET route that loaded the new video form from the views/videos/new.html.erb file with a route that looks like get "/videos/new", to: "videos#new" in the routes.rb file.

Looking at the new action in the videos controller, it's time to come clean. There was really no reason in the previous Rails CRUD lesson with the new action that we needed to hand down this instance variable:

def new
  @video = Video.new
end

It is a Rails convention in the controller for use with the form_for() helper method. What we were doing with @video = Video.new() is creating a blank video object hash in the @video instance variable to pass down to the view: views/videos/new.html.erb.

form_for(@video) can then use blank @video object, which looks like,

@video = {id: "", title: "", description: "", youtube_id: ""}

.. , to create a series of form tags in much the same way we hard code it previously.

form_tag()

Lets back up for one second before we fully look at the form_for() helper method. Think of the form_tag(), as the Rails way of writing slightly less code to build forms.

So we could replace the old basic html form with:

<%= form_tag("/videos", method: "post", authenticity_token: form_authenticity_token) do %>
  <%= label_tag('video[title]', "Title:") %>
  <%= text_field_tag('video[title]') %><br>
  <%= label_tag('video[description]', "Description:") %>
  <%= text_field_tag('video[description]') %><br>
  <%= label_tag('video[youtube_id]', "Youtube ID:") %>
  <%= text_field_tag('video[youtube_id]') %><br>
  <%= submit_tag("submit") %>
<% end %>

which makes to the same form we were using yesterday in the browser:

<form action="/videos" method="post">

  <label for="video_title">Title:</label>
  <input type="text" name="video[title]" id="video_title"><br>

  <label for="video_description">Description:</label>
  <input type="text" name="video[description]" id="video_description"><br>
  
  <label for="video_youtube_id">Youtube ID:</label>
  <input type="text" name="video[youtube_id]" id="video_youtube_id"><br>

  <input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
  <input type="submit" value="submit">
</form>
Notice we added the `` tag today, instead of just typing the word title before the input tag. The label is link to html form elements through the id attribute. Basically it allows a user to click on the label and it makes that linked form element focus.
Replace the plain plain html form in `videos/new.html.erb` with this and load it in your browser. Make sure it still submits to your create function and creates a database record, before you take the next dive into rails bliss.

form_for()

form_for is another helper method that cleans up your form even more, so you can write less code. Go ahead and type the following code in your browser to replace the form_tag code.

<%= form_for(@video) do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %><br />
  <%= f.label :description %>
  <%= f.text_field :description %><br />
  <%= f.label :youtube_id %>
  <%= f.text_field :youtube_id %><br />
<% end %>
Go and inspect the form elements in the chrome inspector. You should notice it is doing many thing we were doing explicitly before, such as putting in authenticity tokens in a hidden field, and putting in all the needed attributes for rails strong params. It even recognizes with the blank video object that we want to post this form to the controller as a new video instead of updating the video.

You should also make the connection at this point that besides all the things we have said, the @video passed in to the form_for method as a parameter is the part of the code that creates the forms action="" attribute. In this case it creates action="/videos".

In the case of a nested object like a user's videos, you would pass in both of the objects that you needed. For example if the route was /users/2/videos/new, and I wanted to also carry along the user who created the video in the post. My form_for() tag might look like:

<%= form_for(@user, @video) do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %><br />
  <%= f.label :description %>
  <%= f.text_field :description %><br />
  <%= f.label :youtube_id %>
  <%= f.text_field :youtube_id %><br />
  <%= submit_tag("submit") %>
<% end %>

It would build the action to be the route it needs to send the request to. It would look like: <form action="/users/2/videos/new">, since we passed in the parameters that rails would need to build the action attribute in the proper order.


Simple_form

There is additional abstraction we can do with forms. Go ahead and add gem 'simple_form' to your Gemfile. Then run bundle install.

Check this out:

You can now replace all that form code for creating a new video with:

<%= simple_form_for(@video) do |f| %>
  <%= f.input :title %>
  <%= f.input :description %>
  <%= f.input :youtube_id %>
  <%= f.button :submit %>
<% end %>

What happened to make all this produce the same thing??? Think of the simple form as everything cool that form_for() was doing, but it is database model aware. This is one of the neat things about abstracting your database into code in your rails app. It can actually use your schema.rb file to figure what data types each database column is, and automatically put in the labels. If you inspect element on this, you will notice too that it puts in maxlength="255". It is even using your database schemas to match up some form validation according to what a database could hold for a text field. Neat!

With each step of form abstraction in rails, know what it is doing. Know how you are abstracting your form tag's action attribute into the objects you are passing into methods like form_for() and simple_form_for().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment