Il s'agit de la partie 7 de la série dans laquelle je documente mon processus d'apprentissage de HTMX avec Django, dans laquelle nous suivrons la documentation de HTMX pour implémenter une fonctionnalité de défilement infini pour les éléments à faire.
Si vous souhaitez consulter le reste de la série, jetez un œil à dev.to/rodbv pour la liste complète.
Lorsque nous implémenterons le défilement infini, nous devrons renvoyer plusieurs éléments de tâche (la prochaine "page" d'éléments) et les charger dans le modèle partiel que nous avons actuellement. Cela signifie changer un peu la façon dont notre modèle partiel est composé ; il est actuellement défini comme décrit dans le diagramme ci-dessous, dans lequel le modèle partiel est responsable du rendu d'un seul élément de tâche :
Nous voulons inverser l'ordre, en ayant le partiel autour de la boucle for :
Effectuons l'échange dans le modèle core/templates/index.html :
<ul> <p>Soon we will get back to the template to add the hx-get ... hx-trigger="revealed" bit that performs the infinite scroll, but first let's just change the view to return several items instead of one on the toggle and create operations:<br> </p> <pre class="brush:php;toolbar:false">... previous code def _create_todo(request): title = request.POST.get("title") if not title: raise ValueError("Title is required") todo = Todo.objects.create(title=title, user=request.user) return render( request, "tasks.html#todo-items-partial", # <-- CHANGED {"todos": [todo]}, # <-- CHANGED status=HTTPStatus.CREATED, ) ... previous code @login_required @require_http_methods(["PUT"]) def toggle_todo(request, task_id): todo = request.user.todos.get(id=task_id) todo.is_completed = not todo.is_completed todo.save() return render( request, "tasks.html#todo-items-partial", # <-- CHANGED { "todos": [todo], # <-- CHANGED }, )
Les tests vérifiant le contenu réussissent toujours et la page a le même aspect, nous sommes donc prêts à implémenter le défilement infini lui-même.
Sur le modèle, nous devons configurer une requête hx-get vers /tasks, avec hx-trigger="revealed", ce qui signifie que la requête GET n'est déclenchée que lorsque l'élément est sur le point d'entrer et devient visible à l'écran ; cela signifie que nous voulons qu'il soit défini après le dernier élément de la liste, et nous devons également indiquer quelle "page" de données nous voulons charger. Dans notre cas, nous afficherons 20 éléments à la fois.
Modifions le modèle en conséquence :
<ul> <p>There's an if next_page_number check around the "loading" icon at the bottom of the list, it will have two purposes: one is to indicate when we're loading more data, but more importantly, when the loader is revealed (it appears on the visible part of the page), it will trigger the hx-get call to /tasks, passing the page number to be retrieved. The attribute next_page_number will also be provided by the context</p> <p>The directive hx-swap:outerHTML indicates that we will replace the outerHTML of this element with the set of <li>s we get from the server, which is great because not only we show the new data we got, but we also get rid of the loading icon. <p>We can now move to the views file.</p> <p>As a recap, here's how the GET /tasks view looks like by now; it's always returning the full template.<br> </p> <pre class="brush:php;toolbar:false">@require_http_methods(["GET", "POST"]) @login_required def tasks(request): if request.method == "POST": return _create_todo(request) # GET /tasks context = { "todos": request.user.todos.all().order_by("-created_at"), "fullname": request.user.get_full_name() or request.user.username, } return render(request, "tasks.html", context)
Il y a déjà un changement effectué dans le code ci-dessus, qui consiste à trier d'abord par tâches les plus récentes ; maintenant que nous nous attendons à avoir une longue liste, cela n'a pas de sens d'ajouter de nouveaux éléments en bas et de les mélanger avec un défilement infini - le nouvel élément finira par être mélangé au milieu de la liste.
Nous devons maintenant différencier les requêtes GET régulières des requêtes HTMX, pour lesquelles nous renverrons simplement une liste de tâches et notre modèle partiel. Il existe une bibliothèque appelée django-htmx qui est très pratique, car elle étend le paramètre request avec des attributs comme request.htmx et les valeurs de tous les attributs hx-*, mais c'est exagéré pour le moment ; Vérifions simplement l'en-tête HTMX maintenant et gérons la pagination à l'aide du paginateur de Django.
# core/views.py ... previous code PAGE_SIZE = 20 ...previous code @require_http_methods(["GET", "POST"]) @login_required def tasks(request): if request.method == "POST": return _create_todo(request) page_number = int(request.GET.get("page", 1)) all_todos = request.user.todos.all().order_by("-created_at") paginator = Paginator(all_todos, PAGE_SIZE) curr_page = paginator.get_page(page_number) context = { "todos": curr_page.object_list, "fullname": request.user.get_full_name() or request.user.username, "next_page_number": page_number + 1 if curr_page.has_next() else None, } template_name = "tasks.html" if "HX-Request" in request.headers: template_name += "#todo-items-partial" return render(request, template_name, context)
La première chose que nous faisons est de vérifier le paramètre de la page et de le définir sur 1 s'il n'est pas présent.
Nous vérifions l'en-tête HX-Request dans la requête, qui nous informera si la requête entrante provient de HTMX et nous permettra de renvoyer le modèle partiel ou le modèle complet en conséquence.
Ce code nécessite certainement quelques tests, mais avant cela, essayons-le. Jetez un œil à l'outil réseau, comment les requêtes sont déclenchées lorsque la page défile, jusqu'à ce que nous atteignions la dernière page. Vous pouvez également voir l'icône animée de « chargement » s'afficher pendant un bref instant ; J'ai limité la vitesse du réseau à 4G pour le rendre visible plus longtemps.
Pour conclure, nous pouvons ajouter un test pour garantir que la pagination fonctionne comme prévu
<ul> <p>Soon we will get back to the template to add the hx-get ... hx-trigger="revealed" bit that performs the infinite scroll, but first let's just change the view to return several items instead of one on the toggle and create operations:<br> </p> <pre class="brush:php;toolbar:false">... previous code def _create_todo(request): title = request.POST.get("title") if not title: raise ValueError("Title is required") todo = Todo.objects.create(title=title, user=request.user) return render( request, "tasks.html#todo-items-partial", # <-- CHANGED {"todos": [todo]}, # <-- CHANGED status=HTTPStatus.CREATED, ) ... previous code @login_required @require_http_methods(["PUT"]) def toggle_todo(request, task_id): todo = request.user.todos.get(id=task_id) todo.is_completed = not todo.is_completed todo.save() return render( request, "tasks.html#todo-items-partial", # <-- CHANGED { "todos": [todo], # <-- CHANGED }, )
C'est tout maintenant ! C'est de loin le plus amusant que j'ai eu avec HTMX jusqu'à présent. Le code complet de cet article est ici.
Pour le prochain article, j'envisage d'ajouter une gestion de l'état du client avec AlpineJS, ou peut-être d'ajouter une fonctionnalité de "date d'échéance". À bientôt !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!