Skip to content

Instantly share code, notes, and snippets.

@wicky-andrian
Forked from jnewman12/Forms.md
Created June 19, 2020 20:04
Show Gist options
  • Save wicky-andrian/ec96adf25677160676010d92a90f0f73 to your computer and use it in GitHub Desktop.
Save wicky-andrian/ec96adf25677160676010d92a90f0f73 to your computer and use it in GitHub Desktop.
Forms, form_for, link_to lesson

Forms, form_for, and link_to


Lesson Objectives

  1. Understand forms and the ways we write them
  2. Understand the difference between form_tag and form_for
  3. Understand link_to and how we can use it to dynamically link our application together

Forms

  • in the most basic way, a form that is not attached to a model looks like this
<%= form_tag do %>
  Form contents
<% end %>

  • When called without arguments like this, it creates a <form> tag which, when submitted, will POST to the current page.
  • For instance, assuming the current page is /home/index, the generated HTML will look like this (some line breaks added for readability):
<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

  • You'll notice that the HTML contains an input element with type hidden. This input is important, because the form cannot be successfully submitted without it.
  • The hidden input element with the name utf8 enforces browsers to properly respect your form's character encoding and is generated for all forms whether their action is "GET" or "POST".
  • The second input element with the name authenticity_token
  • this is a security feature of Rails called cross-site request forgery protection, and form helpers generate it for every non-GET form (provided that this security feature is enabled)

Dynamic Forms

  • a type of form that we might want to have in our application, is one used to provide search functionality.
  • this kind of form would probably contain
    • a form element with "GET" method
    • a label for the input
    • a text input element
    • a submit element

An Example Search form

  • meeting the requirements from above to make our search form, it would look something like this
<%= form_tag("/search", method: "get") do %>
  <%= label_tag(:q, "Search for:") %>
  <%= text_field_tag(:q) %>
  <%= submit_tag("Search") %>
<% end %>

  • the above example will generate html that looks like the following
<form accept-charset="UTF-8" action="/search" method="get">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <label for="q">Search for:</label>
  <input id="q" name="q" type="text" />
  <input name="commit" type="submit" value="Search" />
</form>
  • For every form input, an ID attribute is generated from its name ("q" in above example).
  • These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.
  • Besides text_field_tag and submit_tag, there is a similar helper for every form control in HTML.

form_tag

  • The form_tag helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element's class.
  • since both arguments to form_tag are hashes, you can easily run into a problem if you would like to specify both. For instance, let's say you write this:
form_tag(controller: "people", action: "search", method: "get", class: "nifty_form")

turns into

<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form" method="post">

  • In the example above, method and class are appended to the query string of the generated URL because even though you mean to write two hashes, you really only specified one. So you need to tell Ruby which is which by delimiting the first hash (or both) with curly brackets.
  • This will generate the HTML you expect:
form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_form">

Helpers for Generating Form Elements

  • Rails provides a series of helpers for generating form elements such as checkboxes, text fields, and radio buttons.
  • These basic helpers, with names ending in _tag (such as text_field_tag and check_box_tag), generate just a single <input> element.
  • The first parameter to these is always the name of the input. When the form is submitted, the name will be passed along with the form data, and will make its way to the params in the controller with the value entered by the user for that field.
  • For example, if the form contains <%= text_field_tag(:query) %>, then you would be able to get the value of this field in the controller with params[:query]
  • When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in params.

form helper examples and helpers

  • for most forms, you're probably going to use inputs and text areas. But for every form element you can think of, rails has a way for you to write some ruby to generate your html. This is a list of them;
<%= check_box_tag(:pet_dog) %>
<%= radio_button_tag(:age, "child") %>
<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
<%= date_field(:user, :born_on) %>
<%= datetime_local_field(:user, :graduation_day) %>
<%= month_field(:user, :birthday_month) %>
<%= week_field(:user, :birthday_week) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
<%= color_field(:user, :favorite_color) %>
<%= time_field(:task, :started_at) %>
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
<%= range_field(:product, :discount, in: 1..100) %>

  • The above outputs the following in HTML
<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />
<input id="age_child" name="age" type="radio" value="child" />
<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
<input id="password" name="password" type="password" />
<input id="parent_id" name="parent_id" type="hidden" value="5" />
<input id="user_name" name="user[name]" type="search" />
<input id="user_phone" name="user[phone]" type="tel" />
<input id="user_born_on" name="user[born_on]" type="date" />
<input id="user_graduation_day" name="user[graduation_day]" type="datetime-local" />
<input id="user_birthday_month" name="user[birthday_month]" type="month" />
<input id="user_birthday_week" name="user[birthday_week]" type="week" />
<input id="user_homepage" name="user[homepage]" type="url" />
<input id="user_address" name="user[address]" type="email" />
<input id="user_favorite_color" name="user[favorite_color]" type="color" value="#000000" />
<input id="task_started_at" name="task[started_at]" type="time" />
<input id="product_price" max="20.0" min="1.0" name="product[price]" step="0.5" type="number" />
<input id="product_discount" max="100" min="1" name="product[discount]" type="range" />

Model Object Helpers

  • A particularly common task for a form is editing or creating a model object.
  • While the *_tag helpers can certainly be used for this task they are somewhat verbose as for each tag you would have to ensure the correct parameter name is used and set the default value of the input appropriately.
  • Rails provides helpers tailored to this task. These helpers lack the _tag suffix, for example text_field_tag becomes text_field.

Binding forms to objects

  • as you have already seen, using form_for allows us to bind a form to a specific object and allows us to write complex forms more easily than using the form_tag options.

A MVC form example

  • this would be a typical example of what a form would like look attached to a model
def new
  @article = Article.new
end
<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body, size: "60x12" %>
  <%= f.submit "Create" %>
<% end %>
  • a few things to note about this form

    • @article is the actual object being edited (our model).
    • There is a single hash of options. Routing options are passed in the :url hash, HTML options are passed in the :html hash.
    • The form_for method yields a form builder object (the f variable).
    • Methods to create form controls are called on the form builder object f.
  • the resulting html looks like this

<form accept-charset="UTF-8" action="/articles" method="post" class="nifty_form">
  <input id="article_title" name="article[title]" type="text" />
  <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
  <input name="commit" type="submit" value="Create" />
</form>
  • The name passed to form_for controls the key used in params to access the form's values.
  • Here the name is article and so all the inputs have names of the form article[attribute_name].
  • Accordingly, in the create action params[:article] will be a hash with keys :title and :body.

Forms with Resources

  • When dealing with RESTful resources, calls to form_for can get significantly easier if you rely on record identification.
  • For example, let's say you have an Article model, and resources :articles in your routes.rb file
  • In short, you can just pass the model instance and have Rails figure out model name and the rest:

## Creating a new article
# long-style:
form_for(@article, url: articles_path)

# same thing, short-style (record identification gets used):
form_for(@article)
 
## Editing an existing article
# long-style:
form_for(@article, url: article_path(@article), html: {method: "patch"})

# short-style:
form_for(@article)
  • Notice how the short-style form_for invocation is conveniently the same, regardless of the record being new or existing.
  • Record identification is smart enough to figure out if the record is new by asking record.new_record?. It also selects the correct path to submit to and the name based on the class of the object.

Forms using Nested Resources

  • in the simplest sense, Nested Resources are resources that are children of other resources.
  • for example, a magazine resource may have a nested ad resource like this
class Magazine < ActiveRecord::Base
  has_many :ads
end
 
class Ad < ActiveRecord::Base
  belongs_to :magazine
end
  • Nested routes allow you to capture this relationship in your routing. In this case, you could include this route declaration:
resources :magazines do
  resources :ads
end

  • In the example above, if you wanted to have a form for your ad resource based on your magazine resource, you would do the following

form_for [:magazine, @ad]


How do forms with PATCH, PUT, or DELETE methods work?

  • The Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PATCH" and "DELETE" requests (besides "GET" and "POST").
  • However, most browsers don't support methods other than "GET" and "POST" when it comes to submitting forms
  • Rails works around this issue by emulating other methods over POST with a hidden input named _method, which is set to reflect the desired method
  • so something like this
form_tag(search_path, method: "patch")
  • produces this
<form accept-charset="UTF-8" action="/search" method="post">
  <input name="_method" type="hidden" value="patch" />
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
</form>
  • When parsing POSTed data, Rails will take into account the special _method parameter and act as if the HTTP method was the one specified inside it ("PATCH" in this example)

Link_to


  • link_to creates an anchor element of the given name using a URL created by the set of options.
  • link_to can be written in the following ways
link_to(body, url, html_options = {})
  # url is a String; you can use URL helpers like
  # posts_path

link_to(body, url_options = {}, html_options = {})
  # url_options, except :method, is passed to url_for

link_to(options = {}, html_options = {}) do
  # name
end

link_to(url, html_options = {}) do
  # name
end

Link to examples

link_to "Profile", profile_path(@profile)

produces

<a href="/profiles/1">Profile</a>
  • another way to write this, but specifiing the action and the id manually
link_to "Profile", controller: "profiles", action: "show", id: @profile

produces

<a href="/profiles/show/1">Profile</a>

  • link_to also allows you to write it as a block if your link target is hard to fit into the name parameter
<%= link_to(@profile) do %>
  <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
<% end %>

produces

<a href="/profiles/1">
  <strong>David</strong> -- <span>Check it out!</span>
</a>

  • when using link_to, rails makes it super easy to add custom css attributes to allow you to write your css in an effort to style your application how you see fit
link_to "Articles", articles_path, id: "news", class: "article"

produces

<a href="/articles" class="article" id="news">Articles</a>

  • rails also gives you a way to add anchor links and query strings inside your links
  • this is convenient if you want to link to a certain part of a page (anchor links) or link to a certain search term (query strings)
link_to "Comment wall", profile_path(@profile, anchor: "wall")
# anchor link

produces

<a href="/profiles/1#wall">Comment wall</a>
link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
# query string

produces

<a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
# multiple queries

produces

<a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>

  • adding method to our links allow us to tell rails what HTTP method we want to use. this is especially useful for deleting things
link_to("Destroy", "http://www.example.com", method: :delete)

produces

<a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>

  • you can also use data attributes, which can allow things like a pop up (javascript alert) to give the user more information
link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }

produces

<a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>

  • and finally, rails allows you to set any link attributes such as target, rel, type:
link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"

produces

<a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>

Conclusion

  • form_tag are forms generally used for things that are not attached to models

  • when using a form that we want to bind to a model, we use form_for

  • when using link_to we have a way to do everything from writing dynamically generated links, to custom query strings, and to even tell rails what HTTP method we would like to use

  • some exteneral resources for the curious


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