When developing a Rails application that deals with a lot of form inputs, you’ve probably come across seeing this often.
# Profile form submit
<%= form_tag(@contact) do %>
<%= label_tag (:name, "Name") %>
<%= text_field_tag (:name) %>
<br/>
<%= label_tag (:email, "Email") %>
<%= email_field (:email) %>
<br/>
<%= label_tag (:address, "Address") %>
<%= text_area_tag (:address) %>
<br/>
.........
<%= submit_tag("Submit") %>
<% end %>
As per ruby/rails guides, this is the most basic and most common setup for capturing user input data into our system.
We need to find a way to create one-to-one object mapping between the form fields and their respective ActiveRecord model’s members and properties.
And, we couldn’t have done much better without the help of Rails HTML form helpers to create these data-binding mechanisms within our form input fields against our model objects.
For the majority of the cases, form helpers such as text_field_tag
, email_field
, text_area_tag
are simple to use as we don’t need to provide much configuration properties. They’re just plain text thus Rails can do object-relational mapping easy - without much afterthought.
However, when working with capturing date/time fields such as user’s date of birth, things starting to get more interesting.
If you do something like this
<%= text_field(:date_of_birth) %>
What you’ll end up is you see a text input field that renders the full UTC format of datetime representation, like so
2017-12-12T04:05:06+00:00
This is probably not what you really want to do.
When I searched online for these solutions, there are not many available guides that are deemed useful for our common pattern of use here.
So, I thought I’d share some of my thoughts, findings and learnings.
In the new Rails 4/5, it now comes with several new HTML5 form helper tags we can utilise. We have the following:
select_date
select_datetime
date_select
datetime_select
The two tags with prefixes select_
are mainly used for rendering date and datetime values as a view only, while the others with suffixes _select
are used for creating and updating date and datetime fields that are data-bound to the ActiveRecord model. Thus, it’s important to know this distinction when placing on your forms.
Their simple uses are.
# uses plain ruby DateTime object
<%= select_date(Time.now) %>
vs
# Must have a ActiveRecord Model object to bind
# assuming form is a valid Active Model object with a datetime property called date_of_birth
<%= form.date_select(:date_of_birth) %>
What goes behind the scene of these tag helpers is that Rails handles the view abstraction of rendering HTML5 select tags, usually in three or more. Thus you’ll end up seeing something like this.
<select id=“date_of_birth_year" name=“date_of_birth[year]"> ... </select>
<select id=“date_of_birth_month" name=“date_of_birth_[month]"> ... </select>
<select id=“date_of_birth_day" name=“date_of_birth_[day]"> ... </select>
In addition, these helper tags help you to dynamically populate the select options tags on the fly and pre-select the values so you don’t have to worry about handing date ranges towards the front end, which is pretty neat.
Those tag helpers accept two extra arguments; one for configuring pre-render datetime logic; the other is for toggling HTML attribute options for select
tags. Both them uses ruby hashes.
The following are the common options I used
{prompt: {...}, order: [:day, :month :time]}
prompt
- used for setting up a default option text value for all select tags. They can either be predefined by the helpers, or you can customize them yourself, eg
prompt : {day: 'Day', month: 'Month', Year: 'Year'}
This will set the day, month and year select fields to have their default text values respectively.
order
- for reordering the select tags around.
By default, the normal order, out of the box is year
, month
and day
. So this is very useful if you want to cater the correct locale settings for your user demographics that are most accustomed to.
For HTML attribute options, we have
disabled
- to disabled select for interacting. Good for read-only fields.required
- to make fields required so the user is required to submit these to the form.autofocus
- to give fields autofocus.class
- to add CSS class names for all select tags, useful for styling or user interaction purposes like jQuery.style
- for inline CSS styles for all select tags, though not recommended for best CSS practices.
Putting things all together, we get, eg.
<%= form.date_select(:date_of_birth, {prompt: {day: 'Select day', month: 'Select month', Year: 'Select year'}, order: [:day, :month :time]}, {required: true}) %>
That’s it. This is sufficed enough to meet our main requirements for collecting user’s datetime data.
But you might wonder. What if our requirement is to allow a user to select a specific date range, rather than having to select individual date components like above?
We can use another useful tag helper Rails provides us, which is this.
<%= date_field(:profile, :date_of_birth) %>
However the problem with using date_field
tag is, at the time of writing, it’s not cross-browser compatible yet for Safari and Opera. So you need to bear in mind of your users’ demographic base when they interact your site or app. A better alternative to this is to download and install a jQuery datepicker plugin that does the heavy datepicker functionality for you. Then, you use text_field
tag for the plugin to pick up, as far as back-end implementation is concerned. No cross-browser compatibility issues anymore. Thus, this also does a good job.
Aside from using form helper tags for the presentation layer, what about supporting other formats like JSON/XML?
Simple.
We do the following:
JSON
format.json { render json: Time.now.strftime("%d-%m-%Y %H:%M:%S") }
XML
format.xml { render xml: Time.now.xmlschema } # Alternatively, you can also use stftime format too. They do exactly the same thing.
That’s it.
Till next time.
Happy Coding!