0

I have a People model in my app that's giving me trouble. Each person has three datetime columns :birthday, :anniversary, and :other. I have the form set up like this:

<div class="container">
  <h2 class="text-center">Add a New Person</h2>
  <%= simple_form_for(@person) do |f| %>

    <div class="form-inputs">

      <div class="row">
        <div class="col-xs-12">
          <%= f.label :relationship %>
          <%= f.select :relationship, options_for_select(['Child', 'Spouse', 'Relative', 'Friend', 'Colleague', 'Acquaintance', 'Other'], :selected => f.object.relationship), {}, { :class => 'span3 controls controls-row' } %>
        </div> <!-- col -->
      </div> <!-- row -->

      <div class="row">
        <div class="col-sm-4">
          <%= f.input :first_name, required: true %>
        </div> <!-- col -->
        <div class="col-sm-4">
          <%= f.input :middle_name %>
        </div> <!-- col -->
        <div class="col-sm-4">
          <%= f.input :last_name, required: true %>
        </div> <!-- col -->
      </div> <!-- row -->

      <div class="row">
        <div class="col-sm-4">
          <%= f.label :anniversary %>
          <%= f.text_field :anniversary, class: "form-control datepicker" %>
        </div> <!-- col -->
        <div class="col-sm-4">
          <%= f.label :birthday %>
          <%= f.text_field :birthday, class: "form-control datepicker" %>
        </div> <!-- col -->
        <div class="col-sm-4">
          <%= f.label :other %>
          <%= f.text_field :other, class: "form-control datepicker" %>
        </div> <!-- col -->
      </div> <!-- row -->

      <div class="row">
        <div class="col-sm-12">
          <%= f.input :other_date_name %>
        </div> <!-- col -->
        <div class="col-xs-12">
          <%= f.input :notes %>
        </div> <!-- col -->
      </div> <!-- row -->

    </div> <!-- form inputs -->

    <div class="form-actions">
      <%= f.button :submit, class: "btn btn-outline-primary" %>
    </div>
  <% end %>
</div> <!-- container -->

But this form does get rendered inside a popup on the people#index page, like this:

<div id="newPersonPopup" class="white-popup mfp-hide">
  <%= render 'form', person: @person %>
</div>

I did have a few issues with datepicker, but I followed this question and got it working with the jquery-ui-rails gem.

With this current setup, it saves dates for :birthday and :anniversary but not :other, even though the code is the same.

The relevant controller methods are:

class PeopleController < ApplicationController
  before_action :set_person, only: [:show, :edit, :update, :destroy]
  before_action :authenticate_user!

  # GET /people
  # GET /people.json
  def index
    @people = Person.where(user_id: current_user.id)
    @person = Person.new
  end

  def create
    @person = Person.new(person_params)
    @person.user_id = current_user.id

    respond_to do |format|
      if @person.save
        format.html { redirect_to people_path, notice: 'Person was successfully created.' }
        format.json { render :show, status: :created, location: @person }
      else
        format.html { render :new }
        format.json { render json: @person.errors, status: :unprocessable_entity }
      end
    end
  end


  private
    # Use callbacks to share common setup or constraints between actions.
    def set_person
      @person = Person.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def person_params
      params.require(:person).permit(:relationship, :first_name, :middle_name, :last_name, :birthday, :anniversary, :other, :other_date_name, :notes, :user_id)
    end
  end

I have changed around the order of the fields in the form and gotten different missing attributes (e.g. in some configurations :birthday won't get saved, but :other will, etc.), but nothing I do makes it save all three dates. I have verified that it's saving as nil in rails c.

Can anyone see what's going wonky here?

ADDITIONAL INFORMATION

Here's the server log for the creation of a new person:

Started POST "/people" for 127.0.0.1 at 2018-10-02 20:26:08 -0700
Processing by PeopleController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"o7zlymmDmugZpN94i/xMzbnOCCwh/md0IOzA2W00Lhm8dVTE7bT6ny2BY1aDdqA4ilwenFO9/+XaLdevcyUqeA==", "person"=>{"relationship"=>"Child", "first_name"=>"Test", "middle_name"=>"For", "last_name"=>"Dates", "group"=>"The Testers", "anniversary"=>"10/06/2018", "birthday"=>"10/26/2018", "other"=>"10/13/2018", "other_date_name"=>"Test Date", "notes"=>"Notes go here."}, "commit"=>"Create Person"}
  User Load (3.9ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ /Users/lizbayardelle/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
   (0.1ms)  begin transaction
  ↳ app/controllers/people_controller.rb:39
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/people_controller.rb:39
  Person Create (2.7ms)  INSERT INTO "people" ("relationship", "first_name", "middle_name", "last_name", "anniversary", "other_date_name", "notes", "user_id", "created_at", "updated_at", "group") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["relationship", "Child"], ["first_name", "Test"], ["middle_name", "For"], ["last_name", "Dates"], ["anniversary", "2018-06-10"], ["other_date_name", "Test Date"], ["notes", "Notes go here."], ["user_id", 1], ["created_at", "2018-10-03 03:26:08.941492"], ["updated_at", "2018-10-03 03:26:08.941492"], ["group", "The Testers"]]
  ↳ app/controllers/people_controller.rb:39
   (2.3ms)  commit transaction
  ↳ app/controllers/people_controller.rb:39
Redirected to http://localhost:3000/people
Completed 302 Found in 193ms (ActiveRecord: 9.5ms)


Started GET "/people" for 127.0.0.1 at 2018-10-02 20:26:08 -0700
Processing by PeopleController#index as HTML
  User Load (1.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ /Users/lizbayardelle/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
  Person Load (10.3ms)  SELECT "people".* FROM "people" WHERE "people"."user_id" = ? AND "people"."birthday" IS NOT NULL  [["user_id", 1]]
  ↳ app/controllers/people_controller.rb:9
  Person Load (0.3ms)  SELECT "people".* FROM "people" WHERE "people"."user_id" = ?  [["user_id", 1]]
  ↳ app/controllers/people_controller.rb:11
  Person Load (0.3ms)  SELECT "people".* FROM "people" WHERE "people"."user_id" = ? AND "people"."group" IS NOT NULL  [["user_id", 1]]
  ↳ app/controllers/people_controller.rb:12
  Rendering people/index.html.erb within layouts/application
  Person Load (0.4ms)  SELECT "people".* FROM "people" WHERE "people"."user_id" = ? ORDER BY last_name ASC  [["user_id", 1]]
  ↳ app/views/people/index.html.erb:37
  Person Load (1.8ms)  SELECT "people".* FROM "people" WHERE "people"."user_id" = ? AND "people"."group" IS NULL ORDER BY last_name ASC  [["user_id", 1]]
  ↳ app/views/people/index.html.erb:70
  Person Load (6.2ms)  SELECT "people".* FROM "people" WHERE "people"."user_id" = ? AND "people"."birthday" IS NULL ORDER BY last_name ASC  [["user_id", 1]]
  ↳ app/views/people/index.html.erb:112
  Rendered people/_form.html.erb (165.9ms)
  Rendered people/index.html.erb within layouts/application (267.8ms)
Completed 200 OK in 1219ms (Views: 1167.9ms | ActiveRecord: 20.6ms)
Liz
  • 1,369
  • 2
  • 26
  • 61

1 Answers1

0

The problem is with your date format. Take a look at these lines in your log.

This is what your controller receives:

"anniversary"=>"10/06/2018", "birthday"=>"10/26/2018", "other"=>"10/13/2018"

And this is what your model tries to save:

["anniversary", "2018-06-10"]

So 'October 6' was converted to 'June 10', because the model considered your date as in day/month/year format. And that's the reason why birthday and other dates were ignored. There are no 26th or 13th months, so these attributes were skipped as invalid.

To solve this problem you need to convert the date from the UI into right format (understandable by your model). The best is to convert it in the UI, ie in datepicker. If you want to keep one date format (month/day/year) to be shown to the user for selection purposes and send date to controller in a format recognizable by model (year-month-day), you could use altFormat method. Modify datepicker-related fields in the template:

<div class="row">
  <div class="col-sm-4">
    <%= f.label :anniversary %>
    <input type="text" class="form-control datepicker" data-datepicker-holder="#anniversaryHolder"/>
    <%= f.hidden_field :anniversary, id: "anniversaryHolder" %>
  </div> <!-- col -->
  <div class="col-sm-4">
    <%= f.label :birthday %>
    <input type="text" class="form-control datepicker" data-datepicker-holder="#birthdayHolder"/>
    <%= f.hidden_field :birthday, id: "birthdayHolder" %>
  </div> <!-- col -->
  <div class="col-sm-4">
    <%= f.label :other %>
    <input type="text" class="form-control datepicker" data-datepicker-holder="#otherHolder"/>
    <%= f.hidden_field :other, id: "otherHolder" %>
  </div> <!-- col -->
</div> <!-- row -->

and replace the code which initializes datepicker:

$('input.datepicker').data({behaviour: "datepicker"}).datepicker();

with this:

$('input.datepicker').each(function() {
  $(this).data({behaviour: "datepicker"}).datepicker({altFormat: "yy-mm-dd", altField: $(this).data('datepicker-holder')});
});

Here is a short demo explaining how it works (hidden field is replaced with text field for clarity): http://jsfiddle.net/dx0ufgo8/

Ilya Konyukhov
  • 2,666
  • 1
  • 12
  • 21
  • This makes total sense. Unfortunately, even with that JS it still gives parametrs like this ` Parameters: {"utf8"=>"✓", "authenticity_token"=>"9wqXIJ6iakWV3mTx2wBlLN/Oq4zaaxz6mRrnBZxEDy/TjWVuzPx8jwjqHcYmzsctp9V0qRhl++WxXBvkKE4N6Q==", "person"=>{"relationship"=>"Child", "first_name"=>"Test", "middle_name"=>"Stack", "last_name"=>"Overflow", "group"=>"", "anniversary"=>"10/13/2018", "birthday"=>"10/05/2018", "other"=>"10/20/2018", "other_date_name"=>"SO", "notes"=>""}, "commit"=>"Create Person"}` but only the birthday gets passed to the actual create method and gets saved. – Liz Oct 03 '18 at 19:45
  • Please share your JS code where you initiate that datepicker. – Ilya Konyukhov Oct 03 '18 at 19:49
  • I'm using the `jquery-ui-rails` gem for the datepicker, so there's no JS other than what you gave me, which I put in ` – Liz Oct 03 '18 at 20:23
  • That's strange. Even their documentation suggests that datepicker should be initialized somewhere: https://github.com/jquery-ui-rails/jquery-ui-rails/#warning Nevermind, if datepicker is already initialized when form is rendered, it is possible to alter their options. Could you change that code from ``` – Ilya Konyukhov Oct 03 '18 at 20:54
  • Well, that made the birthday disappear and the anniversary appear. – Liz Oct 03 '18 at 21:20
  • And in a second test the anniversary and other appeared, but not the birthday. – Liz Oct 03 '18 at 21:20
  • Well. I guess the original question is answered. The log shows that dates are not saved due to the wrong format. The format can be changed on datepicker initialization. How to initialize datepicker properly is the next question. Not sure why that initialization is missed (and how it works now without explicit initialization), but you can add it with the short code snippet to your ```application.js``` file: ```$(function() { $(".datepicker").datepicker({ altFormat: "yy-mm-dd" }); });``` – Ilya Konyukhov Oct 03 '18 at 23:19
  • I found the original initialization: `$(function() { $('input.datepicker').data({behaviour: "datepicker"}).datepicker(); });` – Liz Oct 03 '18 at 23:29
  • Cool! So add ```{ alfFormat: “yy-mm-did” }``` into brackets of datepicker() – Ilya Konyukhov Oct 03 '18 at 23:31
  • Okay, it tentatively works as long as you click a date from the picker. If you type something in yourself it goes batty. – Liz Oct 03 '18 at 23:34
  • It looks like ```altField``` option need to be set too. See this answer: https://stackoverflow.com/a/13958379 – Ilya Konyukhov Oct 04 '18 at 00:36
  • Your fiddle works perfectly, but for some reason the new JS made the datepicker stop appearing entirely on the actual app... – Liz Oct 04 '18 at 12:00
  • The reason this wasn't working was that the JS was executing after page load. After some fiddling I used this `$(document).on("ready page:load", function() { $('input.hiddenDateField').each(function() { $(this).siblings('input.datepicker').val($(this).val()); }); $('input.datepicker').each(function() { $(this).data({behaviour: "datepicker"}).datepicker({ altFormat: "yy-mm-dd", altField: $(this).data('datepicker-holder')}); }); });` and it worked perfectly. Thanks for the help! – Liz Oct 06 '18 at 18:49