
Posted on • Edited on • Originally published atalsohelp.com
Rails form : tutorial from the ground
Article was originally published here :https://alsohelp.com/blog/rails-form-tutorial
Introduction : why a new tutorial about forms ?
"Form" is an old standard from the web, and handling form with Rails is almost as old as Rails itself. Sowhy bother about another guide about forms ? They tend to be a lot more complex nowadays. You can submit them via plain browser request, or ajax, TurboDrive, TurboFrame, vanillaJS, and so on. They make the UX and data-flow not so easy to understand, so sometimes it's good to go back to basics :)
Tools used : Rails 6.1.3, Ruby 3
What is a form ?
A web form isnot a Rails concept, a form is a way to send data to a server. There are many other ways to achieve this, but using the<form>
tag means using astandard way to send data. It means excellent device support, excellent accessibility, browser support, and so on.
<formaction="/books"method="post"><inputtype="text"name="book[title]"><inputtype="submit"></form>
This works. Without Rails. Without any JavaScript. The browser itself is able to submit data to an(-other) URL.
So why is Rails needed anyway ?
At first glance we don't need Rails to send any data to the server.
But, to avoid redundant, error-prone copy/pasting, Rails comes with helper that will generate the above form automagically, plus :
- authenticity_token for security reasons,
- consistent classes and ids,
- consistent "name" tags
- ability to disable the submit button once pressed (via JavaScript library namedRails-ujs)
- other goodies (seethis article, for example).
Rails has helpers to build forms :
- form_for (softly deprecated)
- form_tag (softly deprecated)
- form_with (new standard)
You will probably encounterform_for andform_tag inside old gem or Rails projects, butform_with is now the new standard. So, for any new project, the only helper you have to care about isform_with.
Now let's see how Rails take care of the simple form above :
<#ThisiswhatyouwriteinyourRailstemplatefile#><%=form_withscope:"book",url:"/books"do|form|%><%=form.text_field:title%><%=form.submit"Create"%><%end%><#ThisisthegeneratedHTMLyoucanviewinyourbrowser#><formclass="new_book"id="new_book"action="/books"method="post"><inputname="utf8"type="hidden"value="✓"><inputtype="hidden"name="authenticity_token"value="…"><inputtype="text"name="book[title]"id="book_title"><inputtype="submit"name="commit"value="Create"data-disable-with="Create"></form>
Here is what you can notice from here :
- Rails "force" the encoding with a standard utf8, in order to decode your form field properly on server-side,
- The default REST method used is POST, (attribute
method="post"
in the form tag) - There's an hidden field with the authentication_token, for security reason
- The "name" and "id" of the text field are properly written for you,
- The submit field has a name ("commit"), in order to handle the case where there are multiple submit buttons,
- The submit field already has a "data-disable-with" that will be used by Rails-ujs to disable the submit button once pressed.
- Avoiding multiple submit is necessary, think about the case where your customer presses the button "pay", this user probably doesn't want to pay multiple times...
Using formwithout Rails helper is tedious and risky.
Tutorial from scratch
$> rails new myform--minimal$>cdmyform
inside app/controllers/welcome_controller.rb
classWelcomeController<ApplicationController# welcome_path GET /welcome# root_path GET /defindexend# update_book_path POST /welcome/update_bookdefupdate_bookendend
inside app/views/welcome/index.html.erb
<h1>Welcome ! This is a tutorial about Rails forms</h1><%=form_withscope:"book",url:update_book_path,method: :putdo|form|%><%=form.text_field:title%><%=form.submit"Create"%><%end%>
inside config/routes.rb
Rails.application.routes.drawdoget"/welcome",to:"welcome#index"put"/welcome/update_book",to:"welcome#update_book",as:'update_book'root"welcome#index"end
Now run
$> bin/rails server
And open your browser athttp://localhost:3000
When you're working with form in a Rails environment, I suggest always opening the Chrome dev tools console in order to see what's going on in the DOM Tree.
Maybe you'll see some surprises. Here, what we can see is :
Compared to part 1 of this tutorial, this Rails version handles UTF8 in another way. I don't know if it's related to the Rails or Rails-ujs version, but it's kind of funny to notice. It doesn't change anything for the developer, but again, managing forms without Rails helpers is a burden.
Notice the
method: :put
insideapp/views/welcome/index.html.erb
. In order to match the VERB insideroutes.rb
. Finally, thisput
method is insidean hidden field of the form. Probably because most browsers only know how to GET data and POST data, but are unable to use other verbs like DELETE or PUT or PATCH.Everything else behaves as stated above.
Sending data to the server
Inside Chrome dev tools, open the Network tab. Then, type any name for a book inside the form, and submit the form by clicking the button.
We can see that 4 parameters were sent to the server : _method, authenticity_token, book[title], commit
Now open your terminal :
There's only 3 parameters, _method has disappeared, because Rails used it to calculate which method of which Controller was targeted.
So Rails already called the right method for us : WelcomeController#update_book.
And pass 3 parameters into the "params" object (that can be used by any method of the controller) :
- authenticity_token (I never had the use case)
- commit (in case we had multiple submit buttons, but that's not our case)
- book (the useful payload)
Pre-fill the form with default values
The "Rails way" to pre-fill the form with value is to use a Model, which is an object mixed to the database.
If you already have some coding experience, that sounds wrong. Actually, many tutorials do not advise to do so. Instead, you use a plain Model, not mapped to the database, named "form object". I will create a separate blog article about this soon, because this tutorial is already thick enough :)
By now you just have to know that it is possible to do so with Rails, and it brings even more magic (i.e. shorter code) to Rails form.
Unpacking data sent
inside app/controllers/welcome_controller.rb
classWelcomeController<ApplicationController# welcome_path GET /welcome# root_path GET /defindexend# update_book_path POST /welcome/update_bookdefupdate_bookp''p'--- extracted params are ---'pbook_params# will output {"title" => "gatsby"}p''enddefbook_paramsparams.require(:book).permit(:title).to_hendend
Now put "gatsby" in the form and submit the button.
Here is what is printed inside the console.
As you can see, there is no magic here, this time. Worse, you have to deal with "strong parameters", which are here for security reasons, but are somehow annoying because they add a lot of verbosity.
Conclusion
Whereas "web form" is a well-known, old, established web standard that has nothing to do with Rails, using it without any helpers is not advised in a Rails environment. Views and Controllers work together to ensure security, encoding, and routing.
"form_with" is the new unified standard. "form_tag" and "form_for" are deprecated - but kept for compatibility reasons.
Rails automagically maps field names according to the "scope:" key, another way is to inject a "form object" with the "model:" key, we will see how later on.
There is no magic in the other way : once the form is submitted, you have to extract, authorize and read parameters one by one in the controller before working on submitted data.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse