- Understand forms and the ways we write them
- Understand the difference between
form_tag
andform_for
- Understand
link_to
and how we can use it to dynamically link our application together
- 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="✓" />
<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)
- 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
- 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="✓" />
<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
andsubmit_tag
, there is a similar helper for every form control in HTML.
- 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">
- 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 astext_field_tag
andcheck_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.
- 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" />
- 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 exampletext_field_tag
becomestext_field
.
- 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 theform_tag
options.
- 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.
- 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, andresources :articles
in yourroutes.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.
- in the simplest sense,
Nested Resources
areresources
that are children of otherresources
. - for example, a
magazine
resource may have a nestedad
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 yourmagazine
resource, you would do the following
form_for [:magazine, @ad]
- 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="✓" />
<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
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 "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
andquery 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&baz=quux">Nonsense search</a>
- adding
method
to our links allow us to tell rails whatHTTP
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 (javascriptalert
) 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>
-
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 whatHTTP
method we would like to use -
some exteneral resources for the curious