jacob
jacob

Reputation: 541

How do I validate start date and end date?

What's the best way to validate that the end date is not before the start date, and start date is after the end date in Rails?

I have this in my view controller:

<tr>
    <td><%= f.label text="Starts:" %></td>
    <td><%= f.datetime_select :start_date, :order => [:day, :month, :year]%></td>
</tr>
<tr>
    <td><%= f.label text="Ends:" %></td>
    <td><%= f.datetime_select :end_date,:order => [:day, :month, :year]</td>
</tr>

I want it to come up with a popup of sorts, with a meaningful message.

I would like to make a generic method that takes two parameters, start and end date, which I then can call in my viewcontroller ; fx in the code above. Or, do I need to use jQuery instead?

Upvotes: 7

Views: 15397

Answers (5)

veritas1
veritas1

Reputation: 9170

If you want client side validation, use jQuery.

Or in rails, to validate server side, you could create your own I guess?

def date_validation
  if self[:end_date] < self[:start_date]
    errors[:end_date] << "Error message"
    return false
  else
    return true
  end
end

Rails >= 7.0 makes this a one-liner

validates_comparison_of :end_date, greater_than_or_equal_to: :end_date

PR, Rails Guides

Upvotes: 3

Jerome
Jerome

Reputation: 6189

@YaBoyQuy client-side validation can work and avoids a hit to the server...

The question is also about end_date being after the start, so the validation - using gem date_validator - should also state

validates :end_date, presence: true, date: { after_or_equal_to:  :start_date}

The suggestion of

on: :create

would be incorrect for the end_date validation; logically this should also be run on an edit.

I upvoted on the basis of the succinct syntax.

Upvotes: 14

Joshua Pinter
Joshua Pinter

Reputation: 47481

Clean and Clear (and under control?)

I find this to be the clearest to read:

In Your Model

validates_presence_of :start_date, :end_date

validate :end_date_is_after_start_date


#######
private
#######

def end_date_is_after_start_date
  return if end_date.blank? || start_date.blank?

  if end_date < start_date
    errors.add(:end_date, "cannot be before the start date") 
  end 
end

Upvotes: 9

pjammer
pjammer

Reputation: 9577

to use your validates :dt_end, :date => {:after_or_equal_to => :dt_start} you need to have a DateValidator such as this:


class DateValidator > ActiveModel::Validator
  def validate(record)
    the_end = record.dt_end
    the_start = record.dt_start
    if the_end.present?
      if the_end < the_start
        record.errors[:dt_end] << "The end date can't be before the start date. Pick a date after #{the_start}"
      end
    end
  end
end

Upvotes: 2

YaBoyQuy
YaBoyQuy

Reputation: 793

Avoid client side validation, because it only validate client side... Use the rails validaters built in.

  validates :start_date, presence: true, date: { after_or_equal_to: Proc.new { Date.today }, message: "must be at least #{(Date.today + 1).to_s}" }, on: :create
  validates :end_date, presence: true

Upvotes: 6

Related Questions