Maison > développement back-end > Tutoriel Python > Création d'une application To-Do avec HTMX et Django, partie défilement infini

Création d'une application To-Do avec HTMX et Django, partie défilement infini

DDD
Libérer: 2025-01-06 12:41:41
original
323 Les gens l'ont consulté

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.

Mise à jour du modèle partiel pour charger plusieurs éléments

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 :

Creating a To-Do app with HTMX and Django, part infinite scroll

Nous voulons inverser l'ordre, en ayant le partiel autour de la boucle for :

Creating a To-Do app with HTMX and Django, part infinite scroll

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
        },
    )

Copier après la connexion

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.

Implémentation du défilement infini

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.

Creating a To-Do app with HTMX and Django, part infinite scroll

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)
Copier après la connexion

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)
Copier après la connexion

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.

Creating a To-Do app with HTMX and Django, part infinite scroll

Ajout de tests

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
        },
    )

Copier après la connexion

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!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal