Search code examples
javascriptajaxruby-on-rails-6bootstrap5-modal

Rails 6 Bootstrap 5 Ajax Call loading all user_notes instead of just current_user's notes


I have a joined table called user_notes, which joins an adventure_id with a user_id (belongs_to) and has a single longtext field, :note. The problem is that upon loading the page and also with successful ajax calls, every user can see every other user's notes (each user's notes should be private). The JS seems to ignore any proper query in the controller, and even conditionals in the JS do not work. All the ajax and modal/form updating is working great. But any traditional attempt at only showing current_user's notes (with embedded ruby <% if %> wrapping the note partial, for instance) breaks the ajax functionality.

user_notes_controller.rb

  def index
    @adventure = Adventure.find(params[:adventure_id])
    @user = current_user
    @user_notes = current_user.user_notes.where(adventure: @adventure).order(created_at: :desc)
  end

index.html.erb

<div class="container">


  <!-- New Note Form -->
  <div class="row mt-4 card">
    <div class="col-12">
      <h3 class="text-center">Add a New Note</h3>
      <h6 class="text-center">(SHIFT + RETURN/ENTER for a new line)</h6>

      <%= form_with(model: [@adventure, UserNote.new], url: adventure_user_notes_path(@adventure), local: false, method: :post, id: 'note_form') do |form| %>
        <%= form.text_area :note, oninput: 'this.style.height = "";this.style.height = this.scrollHeight + "px"', class: "form-control", placeholder: 'Add a new note...' %>
        <%= form.hidden_field :adventure_id, value: @adventure.id %>
        <%= form.hidden_field :user_id, value: @user.id %>
    </div>
      <div class="d-inline-flex justify-content-center">
        <%= form.submit 'Save Note', class: "btn btn-outline-success text-end" %>
      </div>
  </div>

      <% end %>


      <!-- Display Saved Notes -->
      <div class="container-fluid overflow-auto">
        <div id="user-notes-list">
          <%= render partial: 'user_notes/note', collection: @adventure.user_notes.order(created_at: :desc), as: :note, locals: { adventure_id: @adventure.id, user_id: @user.id } %>
        </div>
      </div>
      <div class="modal fade" id="editNoteModal" tabindex="-1" aria-labelledby="editNoteModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title" id="editNoteModalLabel">Editing Note</h5>
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body" id="editNoteModalContent">
              <!-- Content from _edit_form.html.erb will be loaded here -->
            </div>
          </div>
        </div>
      </div>

</div><!-- End Container -->


<script><!-- Modal for Edit Note Form -->
// Update the list of notes in the DOM after successful Ajax response
$(document).on('ajax:success', '#note_form', function (event, data, status, xhr) {
    $('#editNoteModal').modal('hide'); // Hide the modal

    // Check if the note belongs to the current user
    if (data.user_id === $('body').data('current-user-id')) {
        $('#user-notes-list').html(data); // Update the list of notes
    }
});

$(document).on('click', '.edit-note-btn', function () {
    const noteId = $(this).data('note-id');
    const adventureId = $(this).data('adventure-id');

    $.ajax({
        url: `/adventures/${adventureId}/user_notes/${noteId}/edit_form`,
        method: 'GET',
        dataType: 'html',
        success: function (formHtml) {
            $('#editNoteModalContent').html(formHtml); // Load the form HTML into modal content
            $('#editNoteModal').modal('show'); // Show the modal
        }
    });
});

</script>

_note.html.erb (partial)

<div class="user-note mt-2" id="note_<%= note.id %>">
  <div class="text-dark card d-block p-2"><%= simple_format(note.note) %>
  <!-- Edit and Delete buttons -->
    <button class="btn btn-outline-primary edit-note-btn" data-bs-toggle="modal" data-bs-target="#editNoteModal" data-note-id="<%= note.id %>" data-adventure-id="<%= adventure_id %>">Edit</button>
    <%= link_to 'Delete', adventure_user_note_path(adventure_id: note.adventure, id: note), method: :delete, remote: true, class: 'delete-note btn btn-outline-danger content-justify-center', data: { confirm: 'Are you sure?' } %>
  <!-- end Edit and Delete buttons -->
  </div>
</div>

I've been working on this for about 9 hours now. I've included data-current-user-id="<%= current_user.id %>" in the body tag in the application.html.erb so that the JS could get that passed with conditional IF statements in the JS script; made no difference.

I tried wrapping the _note.html.erb code with <% if note.user_id == @user.id %> then go ahead and display...THAT WORKED!...but, it broke the ajax fluidity and forced a reload of the page to see updated changes. Tried various ways to define @user_notes in the controller; nothing mattered. I tried a host of other fixes (even some recommended by OpenAI), all to no avail.


Solution

  • Fixed. Answer for anyone having similar problems:

    in index.html.erb where we render partial

    <%= render partial: 'user_notes/note', collection: @user_notes, as: :note %>
    

    getting rid of all that local stuff. In the _note partial, changed data-adventure-id's value to <%= note.adventure_id %>. And then made sure that @user_notes = current_user.user_notes.where(adventure: @adventure).order(created_at: :desc) was in each action of the controller (index, create, update, destroy). I tried to clean up the code in the controller by defining that as a before_action, but it broke it again (wouldn't display past user's notes), so I did not follow the DRY convention, and put it back, so now it works beautifully. Hope this helps anyone else trying to integrate AJAX/JS with a joined table.