Difference Between form_form and form_tag in Rails
There are two main ways to build forms in Rails, in this guide we're going to walk through the key differences between using the form_for and form_tag form components in Rails.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

As I mentioned in the intro this section is going to be all about forms so I'm going to pop open our Form Integration click on start and let's take a look at what we have.

medium

So we have form tags. One thing that I did in one of the breaks is I looked at my outline and I realized I had typed in form tag with authenticity token and I wanted to separate that out. So if what you downloaded is slightly different inside of pivotal tracker it's because I moved that into its own little task right down here at the bottom so you can have yours to match that because what we are going to do is we're going to in this guide take a look at the form tag and the form for methods. And then in our deep dive we're going to build a form completely from scratch. Now what we're going to do in this guide is going to add very little actual implementation changes into our app mainly because our form for does everything that we need it to do. It's very efficient but there is this concept of form tags in Rails and it's very important to understand what they do what some of their limits are and it's also good to have an understanding of the process of form tag because it'll help you understand data flow from forms into Rails. And so that is why we're going to go through this process.

Now if I come to the terminal and if you notice right here I have a commit you will not believe this. I filmed a full 25 minute guide on this exact topic about one hour ago and the wrong mic was selected. So all I heard was a bunch of static with just a little bit of my voice. I was not happy about that. Anyway so now we are at it again this is take two on this. I'm going to go through the exact same code so the commit I pushed up will be what we're going to use but that was not one of the fun parts of this series. So I'm gonna pop open sublime here. And let's take a look at what we have an actual I am going to get rid of a few things that I added and we'll add them back in at the end of the guide. But right here I have the form and this is our blog form so you can get to it by going to app/views/blogs/_form_html.erb. Now this all works beautifully and we're eventually going to get to the spot where we're actually using this again. But right now I'm going to come and get rid of all it. (Jordan deleted everything inside of _forms.html.erb) Now, switching back to the terminal looks like the server started up. So if I click on write a new blog here you can see that this is completely empty which is what you'd expect when you get rid of the entire partial. So now with all of that in place let's talk about what we can use instead of form_for. I'm going to implement some embedded Ruby I'm going to say <%= form_tag.

Now let's go and let's open up the form tag documentation. So I have this open all included in the show notes and if you scroll down to where it says the F right here for methods you can see that there is a method called form_tag. Now if you click on that it's going to take you down. And here you can analyze everything that form_tag does, what options it has and then it also describes what the options are. So it has options for :multipart which is something you could use for file uploads. :method this is something very important because the :method is a mapping to the routing system so remember how the routes for creating an item are different than editing. We're going to talk about how that works here with form_tag. :authenticity_token this is something where this is going to be provided by default in our application but eventually in our deep dive we're going to take a look at how this works. :remote this is if we're using javascript and then we have some things such as :enforce_utf8 we don't have to worry about those for this one. Instead, let's take a look at the form_tag just by itself.

So the way that you can read the documentation is the top part is the ruby code and right below it is what it generates. So if you have a form_tag that just passed in a path you'll generate this form for you. So let's test that out I'm going to do form_tag but instead of typing something like this <%= form_tag('/blogs') where it's blogs we can actually take advantage ofblogs_path`.

<%= form_tag(blogs_path)

Now if I hit save let's come back hit refresh and nothing has changed here. But if you click on inspect you'll see that we have a form created and if you click right here it's empty but you can see that we have a form with the action of `"/blogs"'.

large

So this is the same as if we type that in and has the character set of UTF-8 and then a method of "post". So if we open this up the form is pretty much empty from everything we can see. But you can see here that we have an input type of "hidden" which is just the character set of UTF 8.
And then this is pretty cool. This builds in a hidden field called "authenticity" token and passes in a gigantic value. So what this value is is something we'll cover in more depth later. But essentially this is a security token for your app to ensure that the user that is typing this content in is the actual user. For example, if you didn't have something like this then someone could potentially such as a hacker hijack a user's session. And so say they had some type of malware in the background they could come type information in and submit it. Such as into transferring money from one bank account to another. But what this authenticity token does is it protects that so that it generates a new one of these for every time the user comes to the form with every session and that can help protect against malicious users, hackers that kind of thing. So this is important. We'll talk more about it later when we walk through how we can actually generate this manually but form tag does do that for us which is nice.

So switching back into the documentation you could say that is cool but that's kind of pointless we can't submit anything. So moving down you can see that you can actually put a method of put it or you could put a method of get. You could do anything that you wanted there. And so we don't have to do this because by default it generates our method for us. But if you did want to do something like this

<%= form_tag(blogs_path, method: :put) %>

then you would be perfectly fine and capable of doing that for our purposes we don't need this.

Now moving down the next one's multipart we're not going to use that. But this is the one that's going to be very handy is that form_tag takes a block. And if you're not that familiar blocks you can also see it right here. What a block in Ruby allows us to do is to add functionality to the call. So even though this is just a regular method by using a block we can use something like this where I can say do

<%= form_tag(blogs_path) do %>

<% end %>

And and this is how we're going to build our form. So since I want to have our form mimicking what we had before. I'm going to add a class of "form-group" and now inside of this we can start putting in some values using embedded Ruby.

<%= form_tag(blogs_path) do %>
  <div class="form-group">
    <%= label_tag 'title' %>
    <%= text_field_tag ' title' %>
  </div>
<% end %>

And if you're wondering I'm not just making these up this is all in the documentation. So if you scroll all the way up you can see if you go down to L we have a label_tag. If you use the label_tag method and pass in name. It's going to generate this HTML code that says <label for="name"> and it's going to generate that for us you also have some options so, say that you wanted to have something like this where it has the value of the form for label but then you want to pass in an optional value like <%= label_tag 'title' , 'Blog title' %>` then you can do that.

So that's something you can do with label. Additionally you can pass and label tag name and you can even pass a class to it which we don't need to because we're using bootstrap now. Next is the text_field_tag. Let's look at the documentation for that. Scrolling all the way up go down to where it says T. And we have text_field_tag. Now text_field_tag has some more options then label_tag because there's all kinds of things you can do with it. You can do what we just are doing right now which is the base case scenario where we have a text_field_tag were passing a name or well in their case their passing name we're passing title and this is going to generate all of this code for us. Let's test this out if I come back and now hit refresh. You're going to see that we have something that says Blog title but because this is a label because I have title mapped to title even though we have a completely different name for this then when I click on Blog title it activates this which is pretty cool. If I click inspect this is going to show the HTML that was generated.

large

This gives us a type of text, name of title, i.d. of title. Now if we wanted to add a class you may think that we could just do something like this say class: 'form-control'. Now if I come back this isn't going to work because if I hit refresh you can see it populated our form our input with class: 'form-control' with this hash.

medium

So what givs? Well if you come down to input and look at this as a text type it has a title and a name and here the value is {:class=>"form-control"}. So what this tells me is that text_field_tag is a method and as a method it can take arguments. The first argument is what it's going to use to generate the ID and the name for the form. The second one is what it's going to use to grab the value. And if we look at the documentation you can see that this is accurate because looking at number two you can see it says text_field_tag 'query', 'Enter your search query here'. This is the value it translates into the value. So what it thought we were doing is inputting the class as a value. So what you can do is just type nil, <%= text_field_tag 'title', nil, class: 'form-control' %> and then this is all going to work. And if you look at the documentation that's what it recommends to do. This third example as text_field_tag 'search', nil which will be the value and you can see that no value even shows up and then you can add your other options from here. So hitting refresh you can see that. Now this works and it even applied the class of form control. And if you look at it now you have a type, name, I.D., and class.

large

Now if you want to put something in here and you definitely can. So you could say 'Something' that gets passed in. Now if you save and refresh you can see it passes in 'Something' right here. Now passing in a hard-coded text value is probably not what you want to do but what you can do is because we have access to blog.title you could do this.

<%= text_field_tag 'title', blog.title, class: 'form-control' %>

And now if I hit refresh. Nothing changes. This is empty and nothing even populates here but that is because we technically don't have a blog title. If I come back and click edit though now we have our blog title.

medium

So if you want to see how that works from an edit perspective that is when you would most likely use your value. So that is what we want for that one. Now let's come back. We have our title now. We want to use our body and for this we can say Blog content and this is going to be blog.body. But instead of a text_field_tag, we actually want to use a text_area_tag.

<%= label_tag 'body', 'Blog Content' %>
<%= text_area_tag 'body', blog.body, class: 'form-control' %>

Now this is pretty similar if you come up to the top. You can see we have a text_area_tag. This takes in pretty much all of the same kind of items you have the ability to pass in a value we also have the ability to pass in rows which if we are wanting this to match exactly then we're going to want to pass in rows: 15, hitting refresh on our app. You can see that look at that. This is exactly what we had before.

Now we are missing the submit button so let's take care of that. I'm going to copy this. Getting rid of the bottom line because we don't have a label or anything like that and there is a method called submit_tag you can make this 'Save'. And then we can pass in a class: 'btn btn-primary'. Hit refresh and all of this is working. Now let me click on write a new blog I'm going to put this content in here. And now let's see. Do you think the form will work? Well, let me give you a little hint and say no because we're missing something very important and it's part of the reason why I wanted to go through this exercise. It's because we're not going to use a form_tag for our CRUD functionality but by using it'll help you understand the underlying data flow for how this form works and how forms work in Rails, so it's going to be a very helpful exercise. So I'm going to get out of this click save and we have an error and the error is not the most explicit. It says param is missing or the value is empty: blog. So if you look at our blog params

params.require(:blog).permit(:title, :body)

we're saying we want to require the blog and we're permitting a title and a body. Now technically we could just remove the require blog but that's really a bad practice. So let's come down to the parameters and you can see this is what's being sent in the form right here.

large

If you open up the terminal this is also all right here. So you see that we have a parameter and then we have a title a body and everything is there which you would think. What in the world is going on? This seems like it should be working. But that is the issue and that is something that is very common when people are learning Rails is trying to properly understand how Rails interprets data. And I'm going to start up a pry session here to give kind of a base case scenario.

pry

Now I've talked quite a bit about how important the hash data structure is in Rails. Rails loves hashes. Rail's loves key-value pair data stores because it makes it very easy to read data into the system, know which model it belongs to all of that kind of good stuff. So that is the problem and that is the reason why we're getting this error because we're just trying to pass some values. But if you look at our parameters. How in the world would Rails know that we are talking about a blog. There is nothing here. You look at the parameters there is nothing here that references a blog. And that's what the problem is. So how can we do that. Well if I create a hash called blog and I'm going to do it manually I'm going say

blog = Hash.new

This is not with Rails. Notice I didn't open up the rails console this is pure Ruby and I just want to show you the syntax for how Rails is doing this kind of behind the scenes. So here I've created an empty hash and I just called it blog. I could've named it anything I could name it hash I could have called it postGuide whatever. But this is just for explanatory purposes. So if I want to add something to this hash the right syntax would be

blog['title'] = "some title"

And now if I hit return this works. So now if I look at blog it's a hash that is a title and some title. Now if I do the same thing for body so I can say

blog['body'] = "Some content"

Now if I type in blog see how it has a title and a body but I use this blog hash syntax in order to make it work well that is the way that Rails expects data to come in from forms and so that is what we need to update. So right here. Notice how we have all of these just body calls. What we need are body and title calls. What we need is to start using hashes so I'm going to

<%= form_tag(blogs_path) do %>
  <div class="form-group">
    <%= label_tag 'blog[title]', 'Blog title' %>
    <%= text_field_tag 'blog[title]', blog.title, class: 'form-control' %>
  </div

  <div class="form-group">
    <%= label_tag 'blog[body]', 'Blog Content' %>
    <%= text_area_tag 'blog[body]', blog.body, class: 'form-control', rows: 15 %>
  </div>

  <div class="form-group">
    <%= submit_tag 'Save', class: 'btn btn-primary' %>
  </div>
<% end %>

Now I can put in this content notice Nothing's changed on the form. All of this still is exactly the same. But let's take a quick look before we try this and hit inspect. And now notice how this looks.

large

This renders our name as blog title and then it also adds an I.D. of blog_title. This is what Rails looks for and when I re-implement the form_for you'll see exactly that. This is exactly what form_for does for us. Now if I click save now it all works and all of this is doing exactly what we want. So now if I do this and hit save again everything is still working. But let me show you something really quick. So coming back in here you can see that we have all kinds of cool things that we can look at in the terminal.

large

So right here we have parameters so we're taking in the authenticity token the encoding, we took in the body and the title and this did everything that we wanted except something a little bit tricky. Remember how I told you how for updates are form needed to use put well by default for_tag is not using put But it is using post. So let me come back here. Now let's open up the rails console.

rails c

then type

Blog.last

This is going to show you that we have a blog of i.d. 15 with this ("xzcv") title. Now if I look for the one created right before that which I can say

Blog.find(14)

This is the first one we created. So do you notice how interesting that is right here. We have accidentally created a new blog post. Even when we were just trying to edit it and that is because our form_tag by default is going to use method post. And just to give even a little bit more of a reference on why this works like that if we type

rake routes | grep blog

What this is going to do is bring all of our routes in and notice how if you can see right here <%= form_tag(blogs_path) do %> we're passing in the action of form tags so it's going to be blogs path to form tag. And now if you come and see what this looks like

large

in order to create we are using the blogs route now in order to update we're still using the blogs route and when one of the issues with this is the difference between post and put. So that is something very important you don't want to get confused and pass in a putstatement for a post and vice versa. So this is where understanding these verbs is very important. So let me start up the

rails s

and so your question may be and this is a very logical one why in the world do we need form tags, why in the world would we need this. And technically for these kinds of actions, we don't for CRUD functionality you would never want to use a form tag it would be a very bad practice just because of what we just saw with having to declare the method to be clear on. OK, this is when we're going to use put and this is when we're going to use post so it's very important to understand that. Then you also have to hard code hash values and that's never fun. And then you have to manually implement these type of calls to have data for editing. That's also not something that's very fun. So one way you could do this if you use form tag would be to create two forms one for editing one for new actions. That also, is a bad practice because 99 percent of the code would be identical so it's not very DRY. Well that is why for CRUD functionality you always want to use form_for and that's what we are going to revert back to. But one it's very important to understand how a form_tag works because form_tag it kind of removes a little bit of the mystery on how Rails works with forms. Do you notice how (I'm not sure depending on your level of expertise with Rails and prior experience.) Did you even know that this is the syntax you needed in order to insert items and pass them as parameters to Rails. If not that's a very handy thing to know in understanding how data flow works and how parameters are processed. So first it's important to understand that because that can be very helpful then also understanding that these kinds of components are just methods. They're just like the view helper methods that we've created we put in our application helper. And the more practice you can get in passing in arguments and understanding the way those work the easier it's going to be when you're asked to build something you're going to know how it works. When you see a method like this you're going to have a much better understanding of what you need to do in order to manipulate it customize it that kind of thing. Now when would you use a form tag? So we've kind of decided that form_for is definitely the best when we're working directly with a module and with a model type of structure like a blog or a portfolio. Well, form tags come in very handy when they don't have that same direct connection with a model so when I'm usually going to use a form tag it's for doing things such as creating a search engine or doing something that isn't CRUD related. In other words we're not creating, we're not editing, we're not doing the standard database work that we have with blogs. And like we have with portfolios so it's also important to understand when to use the right tools.

So I'm going to get rid of this, and put back form_for

<%= form_for(blog) do |f| %>
  <% if blog.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(blog.errors.count, "error") %> prohibited this blog from being saved:</h2>

      <ul>
      <% blog.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.text_field :title, class: 'form-control', placeholder: "Title" %>
  </div>

  <div class="form-group">
    <%= f.text_area :body, class: 'form-control', rows: 15, placeholder: "Content" %>
  </div>

  <div class="form-group">
    <%= f.submit "Submit", class: 'btn btn-primary' %>
  </div>
<% end %>

and also one of the things that's nice to see is look how much cleaner form_for is form_for dynamically generates our method. So when we are editing an item it's going to know that this needs to relate specifically to the put verb for our route and when we're creating a new one it's going to know to use post. Let's ignore our error messages for right now we're going to learn how to customize those shortly. And coming down we can see our form elements. Now these are a lot cleaner. We don't have to pass the hash value because form_for is a little bit more built out than form_tag. We don't have to have the same level of manual integration. So our text field tag doesn't need to have a hash all we have to do is pass in something like the title. And this is going to give us everything that we need. It's going to one, tell rails that this is going to belong to blog. This is creating all of this for us and it also has the mapping so that when you open this up and edit it's going to pre-populate those edit fields. So I'm going to save this and let's test this hypothesis out. If I click on edit. This pops everything open and it fill fills everything in like we wanted. Now let's inspect and see what this generated. Notice that right here it filled in our value for us which obviously we can see that but it's cool to see in the code. And then also it by default created this same hash syntax for the name and for the ID it gave the blog_title. So everything that we implemented manually Rails is doing with form_for automatically.

And I think that's helpful to understand. Now let's also one thing I want to do is I want to get rid of these labels (Jordan deleted the labels) and this is something that I like to do just because I think it is a cleaner kind of form. And we're going to put a placeholder here and this is going to say 'Title' and we can also add a placeholder here and we're going say 'Content'.

<div class="form-group">
  <%= f.text_field :title, class: 'form-control', placeholder: 'Title' %>
</div>

<div class="form-group">
  <%= f.text_area :body, class: 'form-control', rows: 15, placeholder: 'Content' %>
</div

And what this is going to do is it's going to allow us to no longer have labels. Now if I hit refresh you can see it's gone. And we just have these form fields. If I click write a new blog you can see that we don't need labels because the content placeholders here are already in place and they tell us that this is where a title goes and this is where content goes. And as you type things in it simply overrides the placeholder. So this is different than using a value. Now one last thing I want to lead you with is that we also have the ability if you ever need to populate these fields so the same way that we were able to use and override the values of form tags. We can also do that here. So let's say that you have for your body a kind of template and you want to load in all kinds of data. Well, you can just call value: and this is a method available to form_for and now if I hit refresh this is no longer a placeholder. This is actual content. So this is text content. So it's not where you can see with the placeholder it's simply something that's here that I can't select anything. This is all behind the scenes and as soon as I start typing it overrides it. Now here if I start typing it already pre-populated value inside. So it's important to understand that there is a difference. It's pretty rare that I will hardcode a value in but it is nice to know that that is available to you when you need it. So that is it. That's my second take on that. But it's pretty much identical to what I did the first time. Hopefully my mic is working on this one but this is the two most popular types of forms in rails. The form_tag and the form_for and hopefully now you have a good idea of what the difference is because during your development career you are going to be asked to use both types and for different scenarios and hopefully that gives you a little bit of a clarification on which one is which. So if I come to pivotal tracker I'm going to click off form_tag and form_for and in the next guide we are going to get into how to use javascript form integrations.

Resources