Home > Blockchain >  Combine Django and Bootstrap 5 Modal with Ajax
Combine Django and Bootstrap 5 Modal with Ajax

Time:11-29

How to have button/link that can target object pk like a certain post with {{obj.pk}} then open a modal edit modal and delete modal.

Then the modal can edit and delete successfully without refresh using AJAX (Fetch API or Jquery). I have the problem to target the modal to open the post with certain PK. I cannot use id="editPost{{obj.id}}" or id="deletePost{{obj.id}}" to target the modal because then I need to put the modal in forloop then the modal will be repeated for each post.

Here a some of the code:

{% for obj in page_obj %}
                
            <li>
              <a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#editPost" href="{% url 'post-update' obj.pk %}">Edit Post</a>
            </li>
            <li>
              <a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#deletePost" href="{% url 'post-delete' obj.pk %}">Delete Post</a>
            </li>

            <div id="post{{obj.id}}" class="box-content">
                <p class="box-text mb-2">{{ obj.content }}</p>
                <span class="username">{{ obj.username }}</span>
            </div>


{% endfor %}


            <div class="modal fade" id="deletePost" tabindex="-1">
              <div class="modal-dialog modal-dialog-centered">
                <div class="modal-content">
                  <div class="modal-header">
                    <h5 class="modal-title">Delete Post?</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                  </div>
                  <div class="modal-body">
                    <p>Are you sure you want to delete your post?</p>
                  </div>
                  <div class="modal-footer">
                    <button type="button" class="rvg-button-small" data-bs-dismiss="modal">Cancel</button>
                    <button type="button" class="rvg-button-small">Delete</button>
                  </div>
                </div>
              </div>
            </div>

The way I achieve AJAX is using

<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#editPost" onclick="EditPost('{{ obj.id }}')">
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#deletePost" onclick="DeletePost('{{ obj.id }}')">

const DeletePost = (post_id) => {
fetch("/"   post_id   "/delete/", {
    method: "POST",
    credentials: 'same-origin',
    headers: { "X-CSRFToken": getCookie("csrftoken") },
    })
    .then((res) => res.json())
        .then((data) => {
            document.getElementById(`post${post_id}`).remove()
            console.log("successfully deleted")

    }).catch((e) => alert("Error deleting post."));
}

CodePudding user response:

You can pass arguments to your modal by using data-<something> as well as bootstrap does with data-bs-<something>.

<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#deletePost" data-object-id="{{ obj.id }}" onclick="DeletePost()">

Then with js, inside DeletePost you'll need to extract the pk or any other argument you pass to it. Read more about dataset here.

Another option is to wrap your function with an anonymous function to pass arguments to an inner one and lastly, calling the outer (anonymous) function.

In this case, I use the arrow function syntax but is equivalent to the regular syntax for function definitions:

<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#deletePost"  onclick="(() => { DeletePost({{ obj.id }}) })()">

I would recommend the first option as (I think) it is more flexible for future uses when you require to pass more than one argument to a function but any of them should work.

Edit: I forgot to mention this before. If you use the first option, make sure to pass the event when calling your function in the html element in order to obtain the click target element and the dataset attached to it.

Your element should look like this: <a ... onclick="DeletePost(event)">.

Your js function will receive the event as an argument:

const DeletePost = (event) => {
  const btn = event.target; // the clicked button.
  const object_id = btn.dataset.objectId;
  console.log(object_id); // should display the right id.
}

Edit 2:

In order to be able to call your Django URL within the modal:

  1. Remove the href attribute in the <a> elements inside the for loop or replace the tag <a> for a <button> keeping the attributes that bootstrap needs to show your modal. This will prevent a premature call to your Django view without showing a modal first. Keep also the data-object-id attribute.

  2. In the modal code, replace your <button> elements in the footer with something like this:

<a id="delete-post-btn" class="rvg-button-small" href="{% 'DeletePost' %}">Delete</a>

Keep in mind that at this stage the href is not complete as is missing the object-id.

  1. Create an event listener for the modal, listening to the event 'shown.bs.modal' to know when the modal is open and extract the object id to complete the href attribute:
document
  .getElementById('deletePost')
  .addEventListener('shown.bs.modal', (event) => { 

    // The modal is open and the object-id can be extracted.
    event.preventDefault(); // this step may not be needed.
    const btn = event.relatedTarget; // this is the button that contains the data-<something> attrs
    const objectId = btn.dataset.objectId; 

    // Now we add the object-id value to the href.
    const modal_btn = document.getElementById('delete-post-btn');
    modal_btn.href  = String(objectId);
  }) 

Now the href should be complete and the button should be able to call the Django view URL with the correct object-id.

  • Related