1

I wanted to quickly build a small web app, so I decided to go purely with django and what django offers - template rendering, so no React/Vue or such. The problem that I am having is, that I have some cards, which are being displayed in a for loop (jinja) once the user clicks on a card, a modal shows up and this is where it gets messy.

I want to pass the id of the card, so that I can make a get request and render the data in the modal. The code that I have is:

// index.html

  <div id="machine-cards" class="grid grid-cols-3 gap-4 m-20">
    {% for machine in machines %}
      <div id="card-{{ machine.id }}" onclick="setModalData({{ machine.id }})" class="bg-white shadow-md rounded px-4 pt-4 pb-2 mb-4 transform hover:scale-105 cursor-pointer">
        <div id="card-header" class="flex justify-between">
          <p>Machine number: {{ machine.number }}</p>
          <div id="state"
               class="rounded-full h-4 w-4 {% if machine.status == 1 %} bg-green-500 {% elif machine.status == 2 %} bg-red-500 {% elif machine.status == 3 %} bg-yellow-500 {% else %} bg-gray-500 {% endif %}">
          </div>
        </div>
        <div id="card-body" class="flex justify-center align-center mt-5 mb-5">
          <p>{{ machine.manufacturer }}</p>
        </div>
        <div id="card-footer" class="flex justify-end">
          <p class="text-gray-300 text-sm">Last updated: 28.06.2021</p>
        </div>
      </div>
    {% endfor %}
  </div>
{% include "general/modal.html" %}

<script>
  function setModalData(cardId) {
    // not sure what to do with the cardId :(
    modal.style.display = 'flex';
  }
</script>



// general/modal.html

<body>
<!-- The Modal -->
  <div id="modal" class="hidden justify-center items-center h-full w-full absolute bg-black bg-opacity-50 top-0">
    <!-- Modal content -->
    <div class="bg-white shadow-md rounded px-6 pt-4 pb-8 w-1/2 h-1/2">
      <div class="modal-header flex justify-end">
        <span class="cursor-pointer text-3xl" onclick="modal.style.display = 'none'">&times;</span>
      </div>
      <div class="modal-content flex justify-center items-center ">
        <div class="loader ease-linear rounded-full border-4 border-t-4 border-gray-200 h-12 w-12 mb-4"></div>
      </div>
    </div>

  </div>
</body>
</html>
<script>
  // Get the modal
let modal = document.getElementById("modal");

// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
}
// somehow get the ID, fetch the data and hide the loader
</script>

I know how to do it with React or Vue.js, however, the application is very small and I wanted to quickly prototype it, so I decided to avoid using react/vue or such for now. I was considering giving alpine.js a go as it is smaller compared to the others.

What I have also tried and I hope this is not my best solution, is to move the include block in the for loop and have a hidden modal for every card, which is definitely not the most optimal solution.

dsax7
  • 1,333
  • 22
  • 36
  • You can just add `onclick = setModalData( '{{ machine.id }}');` so this id will be available inside your function. – Swati Jun 29 '21 at 12:24
  • I do have that, not sure what went wrong when copying the code. That will, however, not solve my problem, as even then I will not be able to successfully pass the data to the included html, in my case `general/modal.html`. – dsax7 Jun 29 '21 at 12:27
  • *..even then I will not be able to successfully pass the data ..* you are trying to make ajax called to get data ? – Swati Jun 29 '21 at 12:38
  • What I optimally want to accomplish is to pass the `cardId` to the modal - `{% include "general/modal.html" with cardId=cardId %}` and fetch the data in the script tag of `"general/modal.html"`. However, this does not work. Sorry for not making this clear in the post – dsax7 Jun 29 '21 at 12:44
  • 1
    You can make ajax called and then return that html back .Check [this](https://stackoverflow.com/questions/39727698/how-to-re-render-django-template-code-on-ajax-call) should be helpful. – Swati Jun 30 '21 at 12:56
  • Thank you very much! This worked perfectly (I am even surprised how simple it was and how smooth it runs)! You could post an answer and I will accept it or I could also post what I did – dsax7 Jul 01 '21 at 11:19
  • You can answer your own question ^^ – Swati Jul 01 '21 at 12:10

1 Answers1

0

I went for the suggestion in the comments by @Swati, which requires jquery (it is also possible to do it without, but in my opinion it is not worth it). The part of the the code that change is

// index.html

  // some unchanged html code

  <div class="machine-modal">
    {% include "general/modal.html" %}
  </div>

<script>
  function setModalData(cardId) {
    $(".machine-modal").html('').load(`{% url 'machine_details' %}?id=${cardId}`);
  }
</script>

The view, which is under the machine_details url will render the modal.html with the needed context

def machine_details(request):
    machine_id = request.GET.get('id', None)

    if machine_id is not None:
        machine_data = Machine.objects.get(machine=machine_id)
        return render(request, 'general/modal.html', {'machineDetails': machine_data})

Lastly, the modal is hidden in an if block and shown only when the data is present

// modal.html
{% if machineDetails %}
// html code
{% endif %}
dsax7
  • 1,333
  • 22
  • 36