Time in Text Field in Rails 5

Steps

Step 1

The tasks index page displays a list of tasks with due date and Edit task link in tasks/index.html.erb.

<tbody>
  <% @task_months.each do |month, tasks| %>
    <%= month.strftime('%B') %>
  <tr>
    <% for task in tasks %>
    <div class='task'>
        <strong> task.name </strong>
        <%= task.due_date.to_s(:due_time) %>
    </div>
    <%= link_to 'Edit', edit_task_path(task) %>
    <% end %>
   </tr>         
  <% end %>   
</tbody>

Step 2

Click Edit link to edit task. The due date field has datetime drop down boxes in tasks/_form.html.erb:

<%= form_for(task) do |f| %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :due_date %>
    <%= f.datetime_select :due_date %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Step 3

In the form partial, change the datetime_select to text field.

<%= f.text_field :due_date_string %>

Step 4

Add due_date_string setter and getter methods to the Task model.

class Task < ApplicationRecord
  belongs_to :project
  has_many :comments

  def due_date_string
    due_date.to_s(:db)
  end

  def due_date_string=(due_date_s)
    self.due_date = Time.parse(due_date_s)
  end
end

Step 5

In tasks controller change the task_params method:

def task_params
  params.require(:task).permit(:name, :complete, :due_date_string)
end

Step 6

Reload task edit page http://localhost:3000/tasks/3/edit. Change the date and update the task. It will work.

Step 7

Illegal input throws the following exception:

argument out of range

Let's now fix the invalid input problem.

Step 8

Change the view to use due_date defined in the task model.

<%= form_for(task) do |f| %>
  <div class="field">
   <%= f.label :name %>
   <%= f.text_field :name %>
  </div>
  <div class="field">
   <%= f.label :due_date %>
   <%= f.text_field :due_date %>
  </div>
  <div class="actions">
   <%= f.submit %>
  </div>
<% end %>

Step 9

Change the task_params method as follows:

def task_params
  params.require(:task).permit(:name, :complete, :due_date)
end

Step 10

Remove setter and getter in the task model:

def due_date_string
  due_date.to_s(:db)
end

def due_date_string=(due_date_s)
  self.due_date = Time.parse(due_date_s)
end

I love solutions with less code.

Step 11

Create a custom validator called datetime_validator.rb in the models directory.

class DatetimeValidator < ActiveModel::Validator
  def validate(record)
    Time.parse(record.due_date.to_s)
  rescue ArgumentError
    record.errors[:due_date] << "Invalid datetime format"
  end
end

Step 12

Use our newly defined custom validator in the task model.

class Task < ApplicationRecord
  belongs_to :project
  has_many :comments

  validates_with DatetimeValidator  
end

Edit date with junk as the due_date. You will now see validation error. The user entered old value is also retained.

Summary

In this article, you learned how to use custom validators using ActiveModel validator and validate dates in text field.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.