table row numbering with htmx

Hello,

I am looking for a way to update a forloop.counter for an order item number column in a table made up of row divs. The rows are being added through htmx. The forloop.counter works, but only after refreshing the page, not after adding a new row to the table. I am using row divs instead of table rows, because it was easier (at least to me) to achieve this with htmx.

I’ve been looking for a solution for a few weeks now, but can’t find or think of a solution, so any help will be much appreciated.

The code:

views.py:

def orderitem_update_hx_view(request, parent_id=None, id=None):
    if not request.htmx:
        raise Http404
    try:
        parent_obj = Order.objects.get(id=parent_id)
    except:
        parent_obj = None
    if parent_obj is None:
        return HttpResponse("Not found!")

    instance = None
    if id is not None:
        try:
            instance = OrderItem.objects.get(order=parent_obj, id=id)
        except:
            instance = None 
    form = OrderItemForm(request.POST or None, instance=instance)
    url = reverse("orders:orderitem_update_hx_view_new", kwargs={"parent_id": parent_obj.id})
    if instance:
        url = instance.get_hx_edit_url() 
    context = {
        "url": url,
        "form": form,
        "object": instance
    }
    if form.is_valid():
        new_obj = form.save(commit=False)
        if instance is None:
            new_obj.order = parent_obj
        new_obj.save()
        context['object'] = new_obj
        return render(request, "orders/order_item_inline.html", context)

    return render(request, "orders/order_item_form.html", context)

order-create-update.html:

{% extends 'partials/base.html' %}

{% block content %}

<style>
  .orderitem-form {
    border-bottom: 1px solid black;
  }
</style>

<div class="container-fluid" style="margin-top:80px">
    {% include 'orders/order_form_full.html' %}  
<hr>   


  <div class="row" style="margin-bottom:5px; border-bottom-style:solid; border-bottom-width:1px">
      <div class="col-1" style="font-weight: bold">Item #</div>
      <div class="col-2" style="font-weight: bold">Material Category</div>
      <div class="col-2" style="font-weight: bold">Material Subcategory</div>
      <div class="col-4" style="font-weight: bold">Material</div>
      <div class="col-1" style="font-weight: bold">Quantity</div>
      <div class="col-1" style="font-weight: bold">Price/Unit</div>  
      <div class="col-1" style="font-weight: bold">Action</div>  
  </div>


  {% for orderitem in object.get_orderitems_children %} 
    {{ forloop.counter }}  
    {% include "orders/order_item_inline.html" with object=orderitem %}  
  {% endfor %}

  {% if new_orderitem_url %}  
    <div id="orderitem-create"></div>      
  {% endif %}
  <button type="button" class="btn btn-secondary" hx-get="{{ new_orderitem_url }}" hx-trigger="click" hx-target="#orderitem-create" hx-swap="beforeend">Add order item</button>                                                                                                                                                                                                                                                                               
</div>
{% endblock %}

order_item_inline.html:

<div id="orderitem-{{ object.id }}">
    <div class="row mt-1">        
        
        <div class="col-2">{{ object.material_category }}</div>
        <div class="col-2">{{ object.material_subcategory }}</div>
        <div class="col-4">{{ object.material }}</div>
        <div class="col-1">{{ object.quantity_ordered }}</div>
        <div class="col-1">{{ object.material.price }}</div>  
        <div class="col-1"> 
            <button type="button" class="btn btn-secondary btn-sm" hx-trigger="click" hx-get="{{ object.get_hx_edit_url }}" hx-target="#orderitem-{{ object.id }}">Edit</button>
        </div>           
    </div>
</div>

Thank you.

Always remember that the templates are rendered before the page is sent to the browser. Once the page has been rendered, the rendering engine is done. It makes no sense to talk about the forloop counter or any other template facility in the context of what exists in the browser.

(Side note: Are entries allowed to be deleted? How is that supposed to affect the numbering?)

Since you’re rendering the new rows on the server, it seems to me like you may want to track the number of rows that have been rendered and pass it back as a url parameter. (For example, if you are rendering 5 entries as the initial set, you could build the url passing 6 back to the view for creating the next new entry.)

Thank you for your reply.

It makes no sense to talk about the forloop counter or any other template facility in the context of what exists in the browser.

Yes, it did seem something was off with the counter in this case.

(Side note: Are entries allowed to be deleted? How is that supposed to affect the numbering?)

Yes, the rows are allowed to be deleted.

Since you’re rendering the new rows on the server, it seems to me like you may want to track the number of rows that have been rendered and pass it back as a url parameter. (For example, if you are rendering 5 entries as the initial set, you could build the url passing 6 back to the view for creating the next new entry.)

I understand the idea, but not sure where and how to implement it… Also, in case a row is deleted, does this mean the whole “table” is being re-rendered?

Thanks.

If you’re using a Django formset, they are designed to allow for rows to be deleted. There’s a setting within the table to indicate a row is being deleted - if you combine that with some code to hide the row, it’s as good as deleted.

Physically, it can be done any number of ways. You know how many rows you’re creating initially - say it’s 7. Then, you could render a link in the page that has a url like formset/add/8, indicating that the next row to be added will have id = 8. You return that row, along with an updated url containing formset/add/9.

Thanks again.

The thing is, I am not using formsets. The table row (div) is a form, which is being added to the “table” via htmx.

I was thinking of counting the form instances in the view and then pass the counter to the template… Would that work?

Then you’re effectively trying to do all the work of formsets yourself, replicating the functionality provided by Django.

Remember that each one of the submitted forms will need to be bound to individual forms when the data is posted back to the server.

So it’s not just the templating you need to be concerned with, but also the submission of all the forms.