3

I have a form to create a new source on sources#create on which there is a modal to create a new author. The user can either create a new author to assign to the source or select an existing one from a checklist.

Ideally, if they do need to create a new author, the authors#create form would create the author AJAX-ically and the new author would appear automatically on the checklist of authors that can be associated to the source.

Here is the code for the author selection checkboxes on sources#create:

  <section id="author" class="white h-100">

    <h2>Author(s)</h2>

    <% if @user_authors.count != 0 %>
      <p class="text-center">Select an author to add below or <a data-toggle="modal" data-target="#newAuthorModal">create a new author</a>.</p>
      <div id="#authorsChecklist"><%= render partial: "authors/checklist", locals: { f: f } %></div>
    <% else %>
      <p class="text-center"><a data-toggle="modal" data-target="#newAuthorModal">Create a New Author</a></p>
    <% end %>

  </section>

With this as the authors/_checklist.html.erb partial:

<div class="boxed-scroll">
  <%= f.collection_check_boxes :author_ids, @user_authors.sort_by(&:last_name), :id, :last_first do |b| %>
    <div class="field form-check" style="display: block">
      <%= b.check_box class: "form-check-input" %>
      <%= b.label class: "form-check-label" %>
    </div>
  <% end %>
</div> <!-- boxedscroll -->

And this as the modal to create the author:

<!-- New Author Modal -->
<div class="modal fade" id="newAuthorModal" tabindex="-1" role="dialog" aria-labelledby="newAuthorModalLabel"
  aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="newAuthorModalLabel">Add a New Author</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <%= render partial: "authors/js_form", locals: {author: @new_author} %>
      </div>
    </div>
  </div>
</div>

And here's the create method:

  def create
    @author = Author.new(author_params)
    @author.user_id = current_user.id

    respond_to do |format|
      if @author.save
        format.html { redirect_back(fallback_location: author_path(@author)) }
        format.json { render :show, status: :created, location: @author }
        format.js
      else
        format.html { render :new }
        format.json { render json: @author.errors, status: :unprocessable_entity }
      end
    end
  end

And here's authors/create.js:

  $("#authorsChecklist").html("<%= escape_javascript(render partial: 'authors/checklist', locals: { f: f } ) %>"); 

So far the author does get created successfully:

Started POST "/authors" for ::1 at 2020-01-31 21:23:26 -0800
Processing by AuthorsController#create as JS
  Parameters: {"utf8"=>"✓", "author"=>{"first_name"=>"Liz", "middle_initials"=>"", "last_name"=>"Bayardelle", "notes"=>""}, "commit"=>"Save Author Info"}
  User Load (1.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:5
   (0.1ms)  BEGIN
  ↳ app/controllers/authors_controller.rb:32
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/authors_controller.rb:32
  Author Create (0.8ms)  INSERT INTO "authors" ("first_name", "middle_initials", "last_name", "notes", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["first_name", "Liz"], ["middle_initials", ""], ["last_name", "Bayardelle"], ["notes", ""], ["user_id", 1], ["created_at", "2020-02-01 05:23:26.279449"], ["updated_at", "2020-02-01 05:23:26.279449"]]
  ↳ app/controllers/authors_controller.rb:32
   (0.4ms)  COMMIT
  ↳ app/controllers/authors_controller.rb:32
  Rendering authors/create.js
  Rendered authors/create.js (0.4ms)
Completed 200 OK in 214ms (Views: 27.8ms | ActiveRecord: 42.7ms)

However, the modal doesn't go away when you hit submit and the author doesn't get added to the checkboxes list (basically the partial doesn't refresh). When you manually hit refresh the author appears perfectly.

Can anyone see how to get this to AJAX properly? The desired behavior is for the modal to disappear when you hit submit and for the checklist partial to AJAX-ically refresh so the author appears.

UPDATE

As per comment suggestions, I added this to the create.js, which successfully makes the modal disappear, though the partial did not refresh:

$('#newAuthorModal').modal('hide');

I then found this question which revealed I needed to change create.js to create.js.erb. This somehow stopped the modal from disappearing but yielded a server error saying:

NameError - undefined local variable or method `f' for #<#<Class:0x00007f9e1e0c0ad0>:0x00007f9e18101d38>:
  app/views/authors/create.js.erb:2:in `_app_views_authors_create_js_erb__3387754959944580247_70158492637620'

SECOND UPDATE

After consulting the documentation for collection_check_boxes, the f. before collection_check_boxes is not necessary. I removed it and removed , locals: { f: f } from my create.js.erb. The modal now dismisses properly and the error is gone, but I still have to hit refresh to get the name to appear.

Liz
  • 1,369
  • 2
  • 26
  • 61
  • `$('#modal').modal('hide');` Can u add this into "create.js" – 7urkm3n Feb 01 '20 at 06:01
  • I can't see that you actually have a form anywhere. Where is the local variable `f` in your `authors/create.js` coming from? Why is this not raising a NoMethodError? Is it something simple like you forgot to save the file or the file path / name is wrong? – max Feb 01 '20 at 06:30
  • @7urkm3n I added `$('#newAuthorModal').modal('hide');` and it does successfully dismiss the modal now. Thank you! Now we know at least it's hitting the `create.js`. However, I still have to hit refresh to get the new author to appear in the list. – Liz Feb 01 '20 at 13:25
  • @Liz looks like you have a bad structure, nor i do not understand smth here. – 7urkm3n Feb 02 '20 at 19:24
  • When you render your partial from `create.js.erb`, you aren't doing it within the context of your initial form rendering... ie, `f` doesn't exist.... you will need to consider using check_box_tags instead. – David Feb 04 '20 at 09:15
  • @David How would that work, given that I'm using `collection_check_boxes`? – Liz Feb 04 '20 at 18:54
  • @David I looked at the `collection_check_boxes` documentation and the `f.` is not included. Removing it made the modal dismiss properly, but the name still doesn't appear until I hit refresh. – Liz Feb 04 '20 at 18:59
  • that's the point ... you can't do what you want using collection_check_boxes... collection_check_boxes is a helper method for use in the context of a form ... when you are rendering the partial from authors#create there is no context of that form... you will need to manually build the collection of checkboxes – David Feb 04 '20 at 19:03
  • Sounds like you need to reload the collection trigger the change for the checkboxes. `user_authors .reload`? – Int'l Man Of Coding Mystery Feb 06 '20 at 12:55

1 Answers1

2

In sources#create there was a typo:

<div id="#authorsChecklist">

Should have been:

<div id="authorsChecklist">

And the controller method needed to include the definition of @user_authors:

format.js do
   @user_authors = Author.where(user_id: current_user.id)
end

Now it works!

Liz
  • 1,369
  • 2
  • 26
  • 61