Releasing courses as early access is our way of trying to get the latest updates into your hands as quickly as possible. So while early-access courses aren’t initially 100% complete, our hope is that the early modules give you something to chew on while we incrementally finish up the remaining videos.
Every once in a while during early access we have to go back and change something. That happened this week with the Rails 6 course.
For as long as we can remember, there have been two ways to generate forms in Rails:
form_tag. We’ve been teaching these two methods since rolling out the initial Rails video course in 2013, and even before that in live training sessions. Indeed, the interface for creating forms has been remarkably stable all these years.
At the same time, it has always been problematic to have two ways to do effectively the same thing. And so Rails has been decidedly moving towards a new
form_with method that aims to unify the way all forms are generated. As this seems to be the future of Rails, we’ve gone back through the Rails 6 videos and made updates accordingly. It was a big effort, but we hope you’ll appreciate it.
Now don’t worry: you don’t have to rewatch all the videos! At the end of this email you’ll find a list of videos with specific timecode ranges that have changed. You may just want to rewatch those sections. You also don’t have to use
form_with in order to get on the Rails 6 train. Both
form_tag still work as they always have. However, they’ve been soft deprecated starting in Rails 6 which means in future versions you’ll start to see deprecation warnings.
How Is form_with Different?
So you might be wondering at a high level how using
form_with is different from using
form_tag. Let’s look at a few examples…
Previously, you had two ways to create a form. If the form was for an ActiveRecord model object such as a user stored in the database, then you used
form_for. For example:
<%= form_for(@user) do |f| %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.submit %> <% end %>
This generates a
form tag and yields a form builder object (
f) to the associated block. You then call methods on that form builder object to generate form elements such as labels and input fields.
Sometimes, however, you need to generate a form that’s not bound to an ActiveRecord model object. In those cases, you had to use
form_tag to generate the form. A common example is a “Sign In” form:
<%= form_tag(session_path) do %> <%= label_tag :email %> <%= email_field_tag :email %> <%= label_tag :password %> <%= password_field_tag :password %> <%= submit_tag "Sign In" %> <% end %>
form_tag differs from calling
form_for in two distinct ways: 1) you pass it a URL rather than a model object and 2) it does not yield a form builder to the associated block. As such, you have to use a different set of methods (ending in
_tag) to generate form elements.
form_tag both generate forms, but using two different coding styles. Wouldn’t it be nice if there was one uniform way in Rails to create all forms? Well, that’s the rationale behind the introduction of
form_with, if you have a model you specify it as the value of the
model option, like so:
<%= form_with(model: @user) do |f| %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.submit %> <% end %>
And if you don’t have a model, you call
form_with and pass it the
url option with the URL the form submits to:
<%= form_with(url: session_path) do |f| %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.submit "Sign In" %> <% end %>
In both cases,
form_with yields a form builder object and you generate form elements by calling methods on that object. So the “guts” of the form code are the same regardless of whether you have a model or not. The only difference is whether you specify the
model or the
url option. The overall coding style is consistent.
Now there’s one more important thing you need to know about
form_with. By default, a form generated using
form_with will submit its data using an XHR (Ajax) request. Both
form_tag can do this, too, but you have to set the
remote option to
remote: true is the default.
Using an XHR request by default makes sense given the close relationship Rails has with Turbolinks to speed up navigating between pages. We’ll explore those benefits later on in the course when we get to the topic of Turbolinks. Until then, we think forms are easier to learn if they use the browser’s normal submit mechanism. To do that, we set the
local option to
<%= form_with(model: @user, local: true) do |f| %> ... <% end %> <%= form_with(url: session_path, local: true) do |f| %> ... <% end %>
So that’s a quick glance at the code differences. Despite having to refilm a bunch of videos, we like how
form_with unifies the interface for generating forms. We would have preferred that
form_with not generate a remote form by default, but that’s just our personal preference coming from a teaching perspective.
Videos That Changed
Anyway, as promised here’s a list of the changes we’ve made so you can quickly drop in to each video to see the updates:
The first form we design is for editing events in Module 12 Forms Edit Part 1 at 4:36 - 10:58
The second form is for creating new events in Module 13 Forms Create at 5:52 - 6:49
Next we move the forms into a partial in Module 14 Partials at 2:36 - 3:57
The registration form is next in Module 24 One-To-Many: Forms at 3:56 - 6:07
Then it’s on to the form for creating a user account in Module 27 User Sign Up at 4:30 - 6:04
And of course, we need a form for editing a user account in Module 28 Edit User Account at 1:22 - 3:37
Also, all the exercises and code have been updated to reflect