Mettre à jour l'URL lors de certaines requêtes HTMx #54

Closed
opened 2024-08-18 23:47:16 +02:00 by florian_briand · 8 comments

Décrivez votre idée

Lorsqu'on clique sur un élément du menu, on change "visuellement" de page, mais l'URL n'est pas modifiée.

Une fonctionnalité de HTMx permet de pousser facilement de telles modifications dans l'URL : https://htmx.org/attributes/hx-push-url/ et de pouvoir naviguer dans l'historique facilement.

Par exemple, quand on clic sur l'élément "CPS" dans le menu, cela envoie une requête HTMx sur localhost:8080/pages/cps. Le serveur renvoi quelques éléments HTML, correspondant aux éléments à mettre à jour ; et avec hx-push-url, l'historique est mis à jour et l'URL de la page devient localhost:8080/pages/cps.

Sauf que ... si on rafraichit la page, par exemple, le navigateur charge la page depuis localhost:8080/pages/cps ... qui ne contient pas la raine de la page : seulement les éléments à mettre à jour. FAIL.

Pour gérer ça, il faut observer la présence ou l'absence d'un paramètre dans le header des requêtes : HX-Request n'est présent que si la requête est faite via HTMx.

En théorie, il faudrait donc que, pour toutes les requêtes :

  • Si HX-Request est absent : on renvoie la page HTML complète, comme si on n'utilisait pas HTMX
  • Si HX-Request est présent, là on renvoie juste le bout qui nous intéresse.

Honnêtement ... ça me parait relou à gérer ... Qu'en pensez-vous ?

PS : j'ai poussé des commits implémentant ce hx-push-url et de quoi observer le hx-request sur la branche https://forge.p4pillon.org/P4Pillon/Krys4lide/src/branch/feat/54_update_url_on_navbar_navigation

EDIT : voici l'"essay" HTMX qui parle du sujet : https://htmx.org/essays/template-fragments/

### Décrivez votre idée Lorsqu'on clique sur un élément du menu, on change "visuellement" de page, mais l'URL n'est pas modifiée. Une fonctionnalité de HTMx permet de pousser facilement de telles modifications dans l'URL : https://htmx.org/attributes/hx-push-url/ et de pouvoir naviguer dans l'historique facilement. Par exemple, quand on clic sur l'élément "CPS" dans le menu, cela envoie une requête HTMx sur `localhost:8080/pages/cps`. Le serveur renvoi quelques éléments HTML, correspondant aux éléments à mettre à jour ; et avec `hx-push-url`, l'historique est mis à jour et l'URL de la page devient `localhost:8080/pages/cps`. Sauf que ... si on rafraichit la page, par exemple, le navigateur charge la page depuis `localhost:8080/pages/cps` ... qui ne contient pas la raine de la page : seulement les éléments à mettre à jour. FAIL. Pour gérer ça, il faut observer la présence ou l'absence d'un paramètre dans le header des requêtes : `HX-Request` n'est présent que si la requête est faite via HTMx. En théorie, il faudrait donc que, pour toutes les requêtes : - Si `HX-Request` est absent : on renvoie la page HTML complète, comme si on n'utilisait pas HTMX - Si `HX-Request` est présent, là on renvoie juste le bout qui nous intéresse. Honnêtement ... ça me parait relou à gérer ... Qu'en pensez-vous ? PS : j'ai poussé des commits implémentant ce `hx-push-url` et de quoi observer le `hx-request` sur la branche https://forge.p4pillon.org/P4Pillon/Krys4lide/src/branch/feat/54_update_url_on_navbar_navigation EDIT : voici l'"essay" HTMX qui parle du sujet : https://htmx.org/essays/template-fragments/
florian_briand added the
to-triage
label 2024-08-18 23:47:16 +02:00
florian_briand added this to the 0 - POC project 2024-08-18 23:47:22 +02:00
florian_briand added
enhancement
help wanted
and removed
to-triage
labels 2024-08-18 23:47:34 +02:00
florian_briand added reference feat/54_update_url_on_navbar_navigation 2024-08-19 19:35:34 +02:00
Owner

Je n'avais pas vu ta branche du coup j'ai commencé à implémenter le hr-push-url pour voir ce que ça faisait, mais j'étais pas allé jusqu'à afficher la requête comme toi.

Clairement va falloir le gérer pour plusieurs raisons :

  • pour le reloading sinon ça va être un peu pénible de bosser
  • pour les tests j'imagine que ça va nous rendre service

Dans tous les cas, ça va être indispensable aussi d'avoir rapidement une session côté serveur pour enregistrer le state de l'utilisateur. J'ai du mal à voir comment enregistrer l'état de chaque composant d'une page dans l'url.

Je n'avais pas vu ta branche du coup j'ai commencé à implémenter le `hr-push-url` pour voir ce que ça faisait, mais j'étais pas allé jusqu'à afficher la requête comme toi. Clairement va falloir le gérer pour plusieurs raisons : - pour le reloading sinon ça va être un peu pénible de bosser - pour les tests j'imagine que ça va nous rendre service Dans tous les cas, ça va être indispensable aussi d'avoir rapidement une session côté serveur pour enregistrer le state de l'utilisateur. J'ai du mal à voir comment enregistrer l'état de chaque composant d'une page dans l'url.
Owner

Pour aller plus loin sur ta branche il faudrait peut être :

  • déclarer des urls dans le routeur genre /cps qui reconstruit la page CPS
  • transformer ton hr-push-url='true' en hr-push-url='/cps'

Facile à dire mais la première étape je vois pas trop encore comment faire.

Pour aller plus loin sur ta branche il faudrait peut être : - déclarer des urls dans le routeur genre `/cps` qui reconstruit la page CPS - transformer ton `hr-push-url='true'` en `hr-push-url='/cps'` Facile à dire mais la première étape je vois pas trop encore comment faire.
Author
Owner

Je n'avais, en effet, pas pensé à l'aspect "obligatoire" de hx-push-url pour le hot-reload.

Je pense que le coût de gestion du hx-request est assez énorme si on veut bien l'implémenter et ne pas avoir ces problèmes de hot-reload. À mon avis, plus important que la "javascript fatigue" d'un framework SPA en Typescript.

Ça me fait beaucoup hésiter sur l'idée de poursuivre avec htmx.

Je n'avais, en effet, pas pensé à l'aspect "obligatoire" de `hx-push-url` pour le hot-reload. Je pense que le coût de gestion du `hx-request` est assez énorme si on veut bien l'implémenter et ne pas avoir ces problèmes de hot-reload. À mon avis, plus important que la "javascript fatigue" d'un framework SPA en Typescript. Ça me fait beaucoup hésiter sur l'idée de poursuivre avec htmx.
Author
Owner

J'ai replongé dans les concepts au cœur de HTMx, pour identifier la bonne approche.

Un bon exemple est disponible ici : https://dev.to/apostrophecms/digging-into-htmx-examples-and-how-to-use-it-5cna
Il rappelle qu'en quelque sorte, il faut voir HTMx comme une "surcouche" à une implémentation HTML "old school" :

  • On conçoit un serveur web qui produit des pages selon une arborescence "à l'ancienne", avec une navigation sans aucun appels AJAX, qui recharge une page complète à chaque clic.
  • On rajoute HTMx par dessus, qui va aller charger ces pages complètes mais en ne récupérant que le "fragment" qui correspond à la partie à mettre à jour
  • Puis on optimise côté serveur, pour, sur une requête HTMx, ne faire le rendu que du/des fragment(s) intéressant

L'inconvénient ... c'est que ce n'est pas ce vers quoi l'outillage web a tendu ces 20 dernières années ; donc on manque un peu d'outils pour faire de tels serveurs sans duplication de plein de code.
Côté Python, il y a des librairies qui ont été développées pour aider à gérer des fragments/partials (cf https://github.com/PyHAT-stack/awesome-python-htmx?tab=readme-ov-file#helper-libraries )
Cette discussion est éclairante sur les apports de telles librairies ; et sur les choses qu'on peut faire en Jinja2 "natif" (dont quasiment tout est implémenté dans Askama, le moteur Jinja2 en Rust)

En Rust, dans Askama, on peut essayer d'explorer :

Y'a plus qu'à essayer ...

J'ai replongé dans les concepts au cœur de HTMx, pour identifier la bonne approche. Un bon exemple est disponible ici : https://dev.to/apostrophecms/digging-into-htmx-examples-and-how-to-use-it-5cna Il rappelle qu'en quelque sorte, il faut voir HTMx comme une "surcouche" à une implémentation HTML "old school" : - On conçoit un serveur web qui produit des pages selon une arborescence "à l'ancienne", avec une navigation sans aucun appels AJAX, qui recharge une page complète à chaque clic. - On rajoute HTMx par dessus, qui va aller charger ces pages complètes mais en ne récupérant que le "fragment" qui correspond à la partie à mettre à jour - Puis on optimise côté serveur, pour, sur une requête HTMx, ne faire le rendu que du/des fragment(s) intéressant L'inconvénient ... c'est que ce n'est pas ce vers quoi l'outillage web a tendu ces 20 dernières années ; donc on manque un peu d'outils pour faire de tels serveurs sans duplication de plein de code. Côté Python, il y a des librairies qui ont été développées pour aider à gérer des fragments/partials (cf https://github.com/PyHAT-stack/awesome-python-htmx?tab=readme-ov-file#helper-libraries ) [Cette discussion](https://github.com/mikeckennedy/jinja_partials/issues/1) est éclairante sur les apports de telles librairies ; et sur les choses qu'on peut faire en Jinja2 "natif" (dont quasiment tout est implémenté dans Askama, le moteur Jinja2 en Rust) En Rust, dans Askama, on peut essayer d'explorer : - Les blocks fragments : https://djc.github.io/askama/template_syntax.html#block-fragments - Les fonctions : https://djc.github.io/askama/template_syntax.html#functions - Les templates in templates : https://djc.github.io/askama/template_syntax.html#templates-in-templates - Les macros : https://djc.github.io/askama/template_syntax.html#macros Y'a plus qu'à essayer ...
Author
Owner

J'avoue que mon inquiétude, c'est de réussir à pondre une organisation de fichiers et de code (rust et templates) qui soit clair et sans trop de code boilerplate, pour minimiser au maximum le code redondant / les copier-coller, etc.
Et pour l'instant, c'est pas bien clair dans ma tête comment on va pouvoir réussir ça sans rendre les refacto' de l'interface ingérables

Peut-être qu'une approche pourrait être de commencer avec du copier-coller ... et de voir comment l'optimiser ensuite. Mais ça me parait risqué XD (Cela dit, on est en mode POC, le risque n'est pas réel)

J'avoue que mon inquiétude, c'est de réussir à pondre une organisation de fichiers et de code (rust et templates) qui soit clair et sans trop de code boilerplate, pour minimiser au maximum le code redondant / les copier-coller, etc. Et pour l'instant, c'est pas bien clair dans ma tête comment on va pouvoir réussir ça sans rendre les refacto' de l'interface ingérables Peut-être qu'une approche pourrait être de commencer avec du copier-coller ... et de voir comment l'optimiser ensuite. Mais ça me parait risqué XD (Cela dit, on est en mode POC, le risque n'est pas réel)
Author
Owner

Un "essay" HTMX qui parle du sujet des fragments : https://htmx.org/essays/template-fragments/
Ils suggèrent de plutôt préférer les approches de "block fragment", pour gagner en lisibilité. Mais ça a sans doute ses limites dans des interfaces un peu grosses.

Un "essay" HTMX qui parle du sujet des fragments : https://htmx.org/essays/template-fragments/ Ils suggèrent de plutôt préférer les approches de "block fragment", pour gagner en lisibilité. Mais ça a sans doute ses limites dans des interfaces un peu grosses.
Owner

Alors j'etais tombe sur cela et j'ai l'impression que ca peut correspondre a notre besoin:
https://github.com/robertwayne/axum-htmx

Je vois qu'il a un autre repo: https://github.com/robertwayne/template-axum-htmx-tailwind

Qui propose des exemple de comment utiliser les templates:
https://github.com/robertwayne/template-axum-htmx-tailwind/blob/main/templates/about.html et https://github.com/robertwayne/template-axum-htmx-tailwind/blob/main/src/routes/index.rs

Prenant ca et https://github.com/robertwayne/axum-htmx?tab=readme-ov-file#example-extractors:
je me dit qu'on peut faire un template qui conditionnelement extend le template de la page entiere seulemt si le hx-request header n'est pas present.

Tu en penses quoi? Je loupe un truc?

Alors j'etais tombe sur cela et j'ai l'impression que ca peut correspondre a notre besoin: https://github.com/robertwayne/axum-htmx Je vois qu'il a un autre repo: https://github.com/robertwayne/template-axum-htmx-tailwind Qui propose des exemple de comment utiliser les templates: https://github.com/robertwayne/template-axum-htmx-tailwind/blob/main/templates/about.html et https://github.com/robertwayne/template-axum-htmx-tailwind/blob/main/src/routes/index.rs Prenant ca et https://github.com/robertwayne/axum-htmx?tab=readme-ov-file#example-extractors: je me dit qu'on peut faire un template qui conditionnelement extend le template de la page entiere seulemt si le hx-request header n'est pas present. Tu en penses quoi? Je loupe un truc?
Author
Owner

Merci, c'est de bonnes pistes, en effet !

axum-htmx est une implémentation de helpers comme l'extractor que j'ai mis dans mon exemple. C'est toujours pratique à avoir, surtout qu'il a visiblement géré certains cas un petit peu compliqué.

Par contre ça ne résout, en effet, pas le problème du rendu des partials (et le "guard" qui redirige toute requête non htmx vers "/" c'est un peu une horreur, j'ai l'impression. Sauf peut-être si on inclue dans la requête tout le contexte qui permet de régénérer la "bonne" page, mais ça me parait touchy.

Le template-axum-htmx-tailwind propose également de bonnes pistes ! Je trouve son approche un peu tordue et complexe, mais ... y'a aussi de bonnes idées à piocher, je trouve. À mon avis : à garder sous le coude pour s'en inspirer, mais pas à reprendre tel quel.

Merci, c'est de bonnes pistes, en effet ! `axum-htmx` est une implémentation de helpers comme l'extractor que j'ai mis dans mon exemple. C'est toujours pratique à avoir, surtout qu'il a visiblement géré certains cas un petit peu compliqué. Par contre ça ne résout, en effet, pas le problème du rendu des partials (et le "guard" qui redirige toute requête non htmx vers "/" c'est un peu une horreur, j'ai l'impression. Sauf peut-être si on inclue dans la requête tout le contexte qui permet de régénérer la "bonne" page, mais ça me parait touchy. Le `template-axum-htmx-tailwind` propose également de bonnes pistes ! Je trouve son approche un peu tordue et complexe, mais ... y'a aussi de bonnes idées à piocher, je trouve. À mon avis : à garder sous le coude pour s'en inspirer, mais pas à reprendre tel quel.
Sign in to join this conversation.
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: P4Pillon/Krys4lide#54
No description provided.