Changes in the form are not saved to database

13,064

Solution 1

I just answered a similar question here and have included my answer below:


I also needed to get this working and support multiple locales that do not use US centric date formats, eg: en-AU (Australia) which has the date format dd/mm/yyyy.

The Problem

Rails expects the date format to be yyyy-mm-dd from the browser, yet you want to display the date in the user's locale. Whilst the date control allows you to specify the format for display it does NOT allow you to separately specify the format to be sent back to the server.

The Solution

Build a hidden input field that sends the correct format back to your rails server. I do this with with a custom simple_form input control that builds both the date-picker input field and a hidden field, and use some javascript to convert to the rails date when the input control changes.

The upshot is you can have a nice bootstrap date-picker in rails and use it with simple_form as follows:

<%= f.input :date, as: :bootstrap_datepicker %>

Or to use a long date format:

<%= f.input :date, as: :bootstrap_datepicker, input_html: { format: :long } %>

Implementation

Create or edit the following files:

Gemfile

gem 'bootstrap-sass'
gem 'bootstrap-datepicker-rails'

config/locales/en-AU.yml

en-AU:
  date:
    datepicker:
      default: "dd/mm/yyyy"
      long: "dd MM, yyyy"
    formats:
      default: ! '%d/%m/%Y'
      long: ! '%d %B, %Y'

config/locales/en-US.yml

en-US:
  date:
    datepicker:
      default: "mm/dd/yyyy"
      long: "MM dd, yyyy"
    formats:
      default: "%m/%d/%Y"
      long: ! '%B %d, %Y'

app/assets/stylesheets/application.css.scss

@import "bootstrap-responsive";
@import "bootstrap-datepicker";

app/assets/javascripts/application.js.coffee

#= require bootstrap
#= require bootstrap-datepicker
#= require bootstrap-datepicker-rails

Alternatively, app/assets/javascripts/application.js

//= require bootstrap
//= require bootstrap-datepicker
//= require bootstrap-datepicker-rails

app/assets/javascripts/bootstrap-datepicker-rails.js.coffee

$ ->
  # convert bootstrap-datepicker value to rails date format (yyyy-mm-dd) on our hidden field
  $(document).on 'changeDate', '.bootstrap-datepicker', (evt) ->
    rails_date = evt.date.getFullYear() + '-' + ('0' + (evt.date.getMonth() + 1)).slice(-2) + '-' + ('0' + evt.date.getDate()).slice(-2)
    $(this).next("input[type=hidden]").val(rails_date)

app/inputs/bootstrap_datepicker_input.rb

class BootstrapDatepickerInput < SimpleForm::Inputs::Base
  def input
    text_field_options = input_html_options.with_indifferent_access
    format =  text_field_options.delete(:format)
    hidden_field_options = text_field_options.dup
    hidden_field_options[:class] = text_field_options[:class].dup # so they won't work with same array object
    hidden_field_options[:id] = "#{attribute_name}_hidden"
    text_field_options[:class] << 'bootstrap-datepicker'
    text_field_options[:type] = 'text'
    text_field_options[:value] ||= format_date(value(object), format)
    set_data_option text_field_options, 'date-format', I18n.t(format, scope: [:date, :datepicker], default: :default)
    default_data_option text_field_options, 'provide', 'datepicker'

    return_string =
      "#{@builder.text_field(attribute_name, text_field_options.to_hash)}\n" +
      "#{@builder.hidden_field(attribute_name, hidden_field_options.to_hash)}\n"
    return return_string.html_safe
  end

protected

  def default_data_option(hash, key, value)
    set_data_option(hash,key,value) unless data_option(hash, key)
  end

  def data_option(hash, key)
    hash[:data].try(:[],key) || hash["data-#{key}"]
  end

  def set_data_option(hash, key, value)
    hash[:data].try(:[]=,key,value) || (hash["data-#{key}"] = value)
  end

  def value(object)
    object.send @attribute_name if object
  end

  def format_date(value, format=nil)
    value.try(:strftime, I18n.t(format, scope: [ :date, :formats ], default: :default))
  end
end

Solution 2

I encountered similar problem and converting format in create action solved it for me:

def create
     .....
     @person.dob = DateTime.strptime(params[:person][:dob], '%m/%d/%Y').to_date
     .....
     @person.save
     .....
end
Share:
13,064

Related videos on Youtube

Vitaly
Author by

Vitaly

Updated on September 16, 2022

Comments

  • Vitaly
    Vitaly over 1 year

    I'm trying to add a date field to my form.

    I added bootstrap-datepicker-rails gem, all required css and javascript.

    When I choose the date in the calendar and click Save, it's not updated in the database.

    Here's the code of the form:

    = simple_form_for @model
      = f.input :date_last, :required => true, 
                :input_html => { data: {behaviour: "datepicker"}}, 
                :as => :string
      = f.button :submit, 'Save', :class => 'btn btn-primary'
    

    I think this might have to do with formats, but not sure where to look at.

    Update

    It works when I change default format to yyyy-mm-dd (default is mm/dd/yyyy):

    $('[data-behaviour~=datepicker]').datepicker({"format": "yyyy-mm-dd", "weekStart": 1, "autoclose": true});
    
  • X2theZ
    X2theZ over 10 years
    Awesome answer, thank for that, just pointing out that the date format in the locale should be as follow: ``` date: datepicker: default: "%m/%d/%Y" long: "%B %d, %Y" formats: default: "%m/%d/%Y" long: ! '%B %d, %Y' ``` See apidock.com/ruby/DateTime/strftime
  • Andrew Hacking
    Andrew Hacking about 10 years
    Thanks, edited the answer to reflect the correct format string