1 points par GN⁺ 4 시간 전 | 1 commentaires | Partager sur WhatsApp
  • Tout en conservant le rendu côté serveur basé sur html/template de Go, HTMX ajoute des interactions dynamiques, avec un seul moteur de rendu capable de renvoyer au choix une page complète ou des fragments HTML
  • Le balisage est réparti entre base.tmpl, des templates de page et des fragments réutilisables, et l’usage de embed.FS de Go 1.16 avec un ensemble de templates partagé simplifie le déploiement et l’architecture du rendu
  • Selon HX-Request, les résultats de recherche sont renvoyés sous forme de page complète ou de lignes de tableau, et Vary: HX-Request ainsi que historyRestoreAsHxRequest: false évitent les erreurs de cache et de restauration via le bouton retour
  • Pour rediriger une requête HTMX vers une autre page, il faut utiliser HX-Redirect avec une réponse 2xx plutôt qu’un 3xx classique, et responseHandling pour 204, 422, 4xx et 5xx permet de contrôler la zone remplacée et l’affichage des erreurs
  • Une configuration de départ consiste à désactiver le cache du local storage et l’héritage des attributs, puis à définir un délai maximal pour les requêtes ; à plus grande échelle, on peut ajouter des templates de layout intermédiaires pour prendre en charge des structures d’écran distinctes, comme une zone d’administration

Pourquoi combiner le rendu serveur de Go avec HTMX

  • HTMX permet d’ajouter des interactions dynamiques avec peu de JavaScript, tout en conservant la cohérence et la sécurité du rendu HTML côté serveur offertes par html/template de Go
  • L’implémentation s’articule autour de trois axes
    • une structure de templates prenant en charge à la fois les pages complètes et les réponses HTML partielles
    • la gestion des redirections et des erreurs dans un environnement HTMX
    • la configuration qui modifie les valeurs par défaut de HTMX et les raisons de ces choix
  • L’exemple part d’une fonction basique qui remplace un bouton par une image, puis s’étend à un écran de recherche filtrant une liste pendant la saisie du nom d’utilisateur ou de l’adresse e-mail
  • Le même modèle de templates peut s’appliquer non seulement à HTMX, mais aussi à des outils HTML-over-the-wire comme Unpoly et Hotwire
  • Si vous ne maîtrisez pas encore les bases de HTMX, mieux vaut commencer par consulter la documentation officielle

Structure du répertoire du projet et des fichiers statiques

  • Le projet est divisé en plusieurs zones selon leur rôle
    • assets/html/base.tmpl : squelette HTML partagé par toutes les pages
    • assets/html/pages/ : contenu des pages individuelles
    • assets/html/partials/ : fragments HTML réutilisés à plusieurs endroits
    • assets/static/css, assets/static/img, assets/static/js : ressources statiques
    • cmd/web/ : démarrage du serveur, handlers et code du moteur de rendu HTML
  • Le squelette initial est créé avec les commandes suivantes
go mod init example.com/htmx
mkdir -p assets/static/css assets/static/img assets/static/js assets/html/partials assets/html/pages cmd/web
touch assets/efs.go assets/html/base.tmpl assets/html/partials/images.tmpl assets/html/pages/home.tmpl cmd/web/main.go cmd/web/handlers.go cmd/web/html.go
  • HTMX peut être installé via un CDN ou NPM, mais l’approche privilégiée ici consiste à télécharger une copie puis à la servir directement comme fichier statique de l’application
    • la configuration est plus simple et évite les inconvénients liés à l’usage d’un CDN
    • htmx.org@2.0.10 et le framework CSS sans classes bamboo.css@1.4.0 sont utilisés
wget -P assets/static/js https://cdn.jsdelivr.net/npm/htmx.org@2.0.10/dist/htmx.min.js
wget -P assets/static/css https://cdn.jsdelivr.net/npm/bamboo.css@1.4.0/dist/bamboo.min.css

Séparer les templates de base, de page et partiels

  • base.tmpl définit le squelette global du document, notamment <head>, le titre commun et <main>, puis y insère page:title et page:content
  • Bamboo CSS et le script HTMX sont chargés dans le template de base, et le script HTMX utilise l’attribut defer
    • le navigateur récupère le script en parallèle pendant l’analyse du HTML
    • le script s’exécute une fois l’analyse du HTML et la construction du DOM terminées
  • Tous les templates reçoivent un nom explicite via {{define}}...{{end}}, sans dépendre du nom du fichier
    • le code Go peut ainsi référencer les templates de manière cohérente à partir de leur seul nom défini
    • le : de page:title est un simple séparateur arbitraire ; des noms comme page_title, page-title, pageTitle ou title fonctionneraient aussi
  • Deux comportements sont définis sur le bouton de la page d’accueil
    • hx-get="/gopher" : envoie une requête GET /gopher au clic
    • hx-swap="outerHTML" : remplace le bouton lui-même par le HTML de la réponse
  • partial:image:gopher est un fragment d’image réutilisable, et width="{{.}}" lui transmet la largeur de l’image au moment de l’appel
  • Les fragments HTML utilisés uniquement dans une page donnée peuvent rester dans le fichier de cette page plutôt que dans le répertoire partagé partials
    • l’idée est de placer dans le répertoire commun les fragments utilisés par plusieurs pages, et de garder les fragments propres à une page à côté du contenu concerné

Inclure le HTML et les ressources statiques dans le binaire Go

  • Depuis l’ajout de l’embarquement de fichiers dans Go 1.16, il est possible d’inclure le HTML et les ressources statiques dans le binaire Go plutôt que de les lire sur le disque à l’exécution
  • La directive //go:embed "html" "static" place ces deux répertoires dans embed.FS, puis fs.Sub() les sépare en deux sous-systèmes de fichiers
    • HTMLFiles : système de fichiers des templates avec html comme racine
    • StaticFiles : système de fichiers des ressources statiques avec static comme racine
  • Cette séparation apporte deux avantages
    • le code HTML et le code des fichiers statiques n’accèdent pas inutilement aux fichiers de l’autre domaine
    • il n’est pas nécessaire de préfixer les chemins par html/ ou static/ lors de l’ouverture des fichiers
  • Si l’on préfère éviter panic() dans sub(), on peut retourner une erreur et initialiser les deux variables dans main()
  • Dans l’implémentation actuelle, pour que fs.Sub() échoue, il faudrait que la valeur du répertoire ne soit pas un chemin valide ; comme les chaînes fixes "html" et "static" passent ce contrôle, le risque de panic à l’exécution est très faible

Ensemble de templates partagé et htmlRenderer

  • htmlRenderer gère le flux de rendu de la manière suivante
    • il analyse une seule fois au démarrage un ensemble de templates partagé
    • il clone cet ensemble partagé à chaque rendu
    • il analyse ensuite en plus les templates de page nécessaires à la requête
    • il exécute le template désigné et l’envoie dans la réponse HTTP
  • newHTMLRenderer() enregistre d’abord un template.FuncMap, puis analyse les templates communs
    • now est relié à time.Now
    • d’autres fonctions de template personnalisées peuvent être ajoutées au même endroit
  • render() crée un ensemble de templates par requête avec sharedTemplates.Clone(), puis l’étend avec ParseFS() si additionalTemplateFiles est fourni
  • Les templates sont d’abord exécutés dans un bytes.Buffer, puis le code de statut est écrit avant l’envoi du corps de réponse
  • Lors de l’initialisation du serveur, "base.tmpl" et "partials/*.tmpl" sont analysés dans l’ensemble partagé
    • le template de base et tous les fragments communs sont ainsi toujours disponibles dans le moteur de rendu
    • seuls les templates spécifiques aux pages sont ajoutés au moment de l’appel du handler
  • Les fichiers statiques sont servis via http.FileServerFS(assets.StaticFiles) sur le chemin /static/, et le serveur écoute sur :5051

Rendu de pages complètes et de fragments HTML

  • Le handler de la page d’accueil renvoie le document complet avec l’appel suivant
app.html.render(w, 200, nil, "base", "pages/home.tmpl")
  • Cet appel ajoute pages/home.tmpl à l’ensemble partagé, puis exécute le template base avec un 200 OK
  • Le handler /gopher n’analyse aucun fichier supplémentaire et exécute uniquement partial:image:gopher, déjà inclus dans l’ensemble partagé
width := 100
app.html.render(w, http.StatusOK, width, "partial:image:gopher")
  • HTMX remplace le bouton par l’image reçue depuis /gopher, conformément au paramètre hx-swap="outerHTML"
  • Cette structure offre les avantages suivants
    • Les templates et les ressources statiques sont inclus dans le binaire, ce qui facilite le déploiement
    • Le même render() peut renvoyer à la fois une page HTML complète et un fragment HTML spécifique
    • Les templates de base et partiels réduisent la duplication du balisage
    • Les templates partiels peuvent être réutilisés dans le template de base, le contenu des pages et d’autres templates partiels

Recherche d’utilisateurs avec mise à jour à la saisie

  • La fonctionnalité de recherche est divisée en deux routes
    • GET /users : une page HTML complète contenant les informations de tous les utilisateurs
    • GET /users/search : un fragment HTML qui ne renvoie que les lignes du tableau des utilisateurs dont le nom ou l’e-mail correspond à la requête
  • Le champ de recherche combine les attributs HTMX suivants
    • hx-get="/users/search" envoie la requête de recherche
    • hx-trigger="input changed delay:500ms, keyup[key=='Enter']" déclenche la requête 500 ms après une modification de saisie ou lorsqu’on appuie sur Entrée
    • Le terme recherché est transmis sous forme de chaîne de requête, par exemple GET /users/search?query=foo
    • hx-target="#search-results" insère la réponse dans <tbody id="search-results">
    • hx-push-url="true" met à jour la barre d’adresse à chaque requête et ajoute une entrée à l’historique du navigateur
  • users:rows est un template séparé qui ne rend que les lignes des utilisateurs
    • Chaque ligne affiche le nom et l’e-mail
    • Si IsGopher est vrai, partial:image:gopher est rendu avec une largeur de 24
  • listUsers et searchUsers ajoutent tous deux pages/users.tmpl à l’ensemble partagé, mais n’exécutent pas le même template
    • listUsers : exécute base et renvoie la page complète
    • searchUsers : exécute users:rows et ne renvoie que les lignes correspondantes
  • Si la requête de recherche est vide, la liste complète des utilisateurs est utilisée ; si une valeur est fournie, strings.Contains() vérifie le nom et l’e-mail
  • Dans une application réelle, on peut envisager les améliorations suivantes
    • Fusionner les handlers de liste et de recherche en un seul
    • Centraliser les helpers de gestion d’erreurs et de logging
    • Ajouter via un middleware les en-têtes Content Security Policy et la récupération après panic
    • Définir correctement les délais d’expiration côté serveur

Distinguer page complète et réponse partielle avec HX-Request

  • Si un utilisateur ouvre directement /users/search?query=leo dans son navigateur ou visite un lien partagé, il risque de ne voir que le HTML partiel correspondant aux lignes du tableau
  • Les requêtes HTMX incluent toujours l’en-tête HX-Request: true, qui sert donc à déterminer le type de réponse
func isHTMXRequest(r *http.Request) bool {
    return r.Header.Get("HX-Request") == "true"
}
  • Le template exécuté par défaut du handler de recherche est base, puis il ne passe à users:rows que pour les requêtes HTMX
    • En accès direct, on reçoit la page complète avec les résultats de recherche
    • Pour une requête HTMX, seule la portion de lignes à insérer dans <tbody> est renvoyée

En-tête Vary et restauration avec le bouton Retour

  • Comme une même URL peut renvoyer des réponses différentes selon la valeur de HX-Request, il faut aussi signaler cette différence au cache
  • Ajouter Vary: HX-Request à toutes les réponses rendues
    • Le définir uniquement dans les handlers qui en ont besoin est, à strictement parler, un peu moins coûteux
    • Le définir globalement dans le renderer évite les bugs potentiels dus à un oubli
  • Lorsque hx-push-url ou hx-boost ajoute des entrées à l’historique du navigateur, HTMX met en cache le HTML complet de la page dans le stockage local du navigateur
    • Le cache par défaut est limité à 10 pages
    • Si l’on revient en arrière jusqu’à un point absent du cache, HTMX redemande l’URL au serveur
  • Par défaut, les requêtes de restauration incluent HX-Request: true, ce qui peut conduire le serveur à renvoyer du HTML partiel au lieu d’une page complète
  • Si le type de réponse est choisi à partir de HX-Request, il faut définir historyRestoreAsHxRequest: false
    • Cela retire HX-Request: true des requêtes de restauration après un cache miss
    • Le serveur renverra alors une page HTML complète pour cette requête
  • La configuration HTMX est placée dans la balise meta htmx-config de base.tmpl

Rediriger une requête HTMX vers une autre page

  • HTMX peut remplacer immédiatement un fragment HTML, comme un message de succès, ce qui réduit le besoin du flux classique Post/Redirect/Get
  • Quand il faut envoyer l’utilisateur vers une page complètement différente, comme vers son profil après une connexion réussie, une simple réponse 3xx ne suffit pas
    • Le navigateur suit la réponse 3xx avant HTMX
    • HTMX ne voit que la réponse finale après redirection et remplace le contenu de la cible existante avec ce contenu
  • Pour une requête HTMX, il faut utiliser une réponse 2xx avec l’en-tête HX-Redirect
    • HX-Redirect: /foo/bar fait naviguer le navigateur vers cette URL et recharge la page complète
    • La requête vers la destination n’inclut pas HX-Request: true
  • Si l’on veut aussi prendre en charge les environnements où JavaScript est désactivé ou où HTMX n’est pas chargé, il faut conserver une réponse 3xx classique pour les requêtes non HTMX
func redirect(w http.ResponseWriter, r *http.Request, url string, code int) {
    if isHTMXRequest(r) {
        w.Header().Set("HX-Redirect", url)
        w.WriteHeader(http.StatusNoContent)
        return
    }

    http.Redirect(w, r, url, code)
}
  • Pour aller vers /profile après la connexion, on peut l’appeler ainsi
redirect(w, r, "/profile", http.StatusSeeOther)

Différence entre HX-Redirect et HX-Location

  • HX-Location réalise un comportement proche d’une redirection sans recharger toute la page
    • Il récupère le HTML de l’URL cible
    • Il remplace la réponse dans le <body>
    • Il ajoute une nouvelle entrée à l’historique du navigateur
  • Lors de la requête vers l’URL cible, HX-Request: true est inclus
  • Cela peut poser problème si la route cible renvoie du HTML partiel selon HX-Request
    • Le handler ne peut pas distinguer une requête issue de HX-Location d’une requête HTMX classique
    • La première nécessite une page complète, la seconde une réponse partielle
  • Contrairement à la restauration d’historique, il n’existe pas de réglage permettant de désactiver HX-Request: true pour une navigation HX-Location
  • Dans la plupart des cas, il est plus simple et plus sûr d’utiliser HX-Redirect, quitte à recharger la page complète
  • Si la route cible est configurée pour toujours renvoyer du HTML complet, HX-Location peut aussi convenir

Gestion des réponses 204·422·4xx·5xx

  • Par défaut, HTMX ne remplace pas le DOM pour les réponses 4xx ou 5xx
    • L’écran actuel reste tel quel
    • L’erreur est consignée dans la console des outils de développement
  • Même si le serveur renvoie 500 Internal Server Error à cause d’un nom de template inexistant, l’utilisateur ne voit généralement pas l’erreur à l’écran avec la configuration par défaut
  • Pour afficher les messages d’erreur à l’utilisateur, les réponses 4xx et 5xx sont configurées pour remplacer l’ensemble du <body>
  • L’exception est 422 Unprocessable Content
    • Il est utilisé pour renvoyer un formulaire contenant des erreurs de validation
    • La réponse doit remplacer normalement la cible HTMX existante
  • responseHandling est configuré ainsi selon les codes d’état
    • 204 No Content : ne modifie pas le DOM
    • 422 Unprocessable Content : remplace normalement la cible existante
    • Les autres 4xx et 5xx : remplacent la réponse dans <body>
    • Les autres réponses : remplacent normalement la cible existante
"responseHandling":[
    {"code":"204", "swap": false},
    {"code":"422", "swap": true},
    {"code":"[45]..", "swap": true, "target": "body"},
    {"code":"...", "swap": true}
]
  • Si, dans une interaction donnée, les erreurs doivent être insérées dans une cible autre que <body>, il est possible de remplacer le comportement par défaut avec l’extension response target

Vérifier l’URL actuelle dans la barre d’adresse du navigateur

  • HTMX ne modifie pas l’URL du navigateur pendant une requête AJAX et le remplacement de la réponse si hx-boost, hx-push-url et hx-replace-url ne sont pas utilisés
  • Pour cette raison, r.URL dans le gestionnaire Go peut être différente de l’URL que l’utilisateur voit dans la barre d’adresse
  • HTMX transmet l’URL actuellement affichée par le navigateur dans l’en-tête HX-Current-URL de chaque requête
  • La fonction utilitaire analyse cet en-tête en url.URL et renvoie r.URL si l’en-tête est absent
func browserURL(r *http.Request) (*url.URL, error) {
    cu := r.Header.Get("HX-Current-URL")
    if cu != "" {
        return url.Parse(cu)
    }

    return r.URL, nil
}

Configuration qui modifie les valeurs par défaut de HTMX

  • Avec historyCacheSize: 0, le cache de pages en local storage de HTMX est entièrement désactivé
    • Le cache en local storage peut être une source de bugs et de problèmes de sécurité
    • Quand on clique sur retour, le HTML est rechargé depuis le serveur au lieu d’être pris depuis le cache
    • Dans les futures versions de HTMX, le cache en local storage devrait aussi être désactivé par défaut pour la même raison
  • Avec disableInheritance: true, l’héritage des attributs HTMX est désactivé
    • Il est plus clair de toujours déclarer les attributs explicitement
    • Cela réduit le risque de bugs ou de comportements non intentionnels
    • Dans les futures versions de HTMX, l’héritage des attributs devrait lui aussi être désactivé par défaut
  • Avec includeIndicatorStyles: false, HTMX n’injecte pas les styles d’indicateur
    • Les styles d’indicateur sont définis manuellement avec les autres règles CSS pour garder une présentation cohérente
  • Par défaut, les requêtes HTMX n’ont pas de délai d’expiration et attendent jusqu’à ce que le serveur réponde
    • Selon la manière dont l’application Go gère les timeouts et les deadlines, vous pouvez définir timeout en millisecondes
    • Le point de départ proposé est timeout: 5000
  • La configuration par défaut inclut aussi historyRestoreAsHxRequest: false ainsi qu’un responseHandling selon les codes d’état
<meta
    name="htmx-config"
    content='{
        "includeIndicatorStyles": false,
        "historyCacheSize": 0,
        "historyRestoreAsHxRequest": false,
        "responseHandling":[
            {"code":"204", "swap": false},
            {"code":"422", "swap": true},
            {"code":"[45]..", "swap": true, "target": "body"},
            {"code":"...", "swap": true}
        ],
        "timeout": 5000
    }'
>

Mise en page par page pour les grandes applications

  • Si le template de base et les templates propres à chaque page ne suffisent pas, il est possible d’ajouter un template de layout entre ces deux niveaux
  • C’est adapté à des sections comme une page d’administration, qui nécessitent un titre, un menu de navigation et une structure de contenu différents des pages ordinaires
  • base appelle layout au lieu d’appeler directement page:content
<body>
    {{template "layout" .}}
</body>
  • layouts/admin.tmpl définit la structure commune de la zone d’administration et appelle page:content à l’intérieur
    • Titre de la zone d’administration
    • Menu de navigation vers les utilisateurs et les commandes
    • <main> destiné à accueillir le contenu propre à chaque page
  • Le gestionnaire transmet à render() à la fois le layout à utiliser et le template de page
app.html.render(
    w,
    200,
    nil,
    "base",
    "layouts/admin.tmpl",
    "pages/admin-orders.tmpl",
)
  • Cela permet de combiner un template de base partagé avec des layouts par parcours et du contenu de page, et d’étendre le même modèle de rendu à des écrans qui nécessitent une structure distincte, comme la zone d’administration

1 commentaires

 
GN⁺ 4 시간 전
Avis sur Hacker News
  • Aime Go + HTMX, et l’utilise avec a-h/templ pour renforcer la sûreté de typage des templates, composants et fragments HTML A publié cet ensemble d’outils, composé de Go, Unix et SQLite, sous le nom de stack GUS : https://housecat.com/blog/the-gus-stack-go-unix-sqlite Il s’inspire fortement de la stack GUTS d’exe.dev, mais utilise HTMX au lieu de TypeScript : https://exe.dev/docs/guts Utilise cockroachdb/errors pour les erreurs avec stack trace, templ pour le HTML type-safe, fuego pour générer les spécifications OpenAPI, sqlc pour générer le code SQL, modernc.org/sqlite pour SQLite en pur Go, goose pour les migrations, dbos pour les workflows de persistance, et rod pour les tests et l’automatisation Chrome/CDP Cette combinaison est productive aussi bien pour coder à la main ou avec l’aide d’un agent que pour construire et déployer un binaire unique

    • Regrette que Hyperscript ne reçoive pas l’attention qu’il mérite quand on parle de HTMX Il permet de manipuler le DOM sans aller-retour serveur ni fonction JavaScript séparée, s’accorde très bien avec Go + HTMX, et donne l’impression d’être une extension du HTML sur lequel on travaille déjà Certaines personnes n’aiment peut-être pas la programmation déclarative, mais sur Open Payment Host, qui gère plusieurs passerelles de paiement, même des manipulations DOM complexes ont pu être implémentées uniquement avec Hyperscript https://hyperscript.org/, https://openpaymenthost.com/
    • On pourrait aussi renommer l’ensemble stack HUGS, pour HTMX ou hypertexte, Unix, Go et SQLite
    • A aussi vu Go, Templ et HTMX appelés stack GoTH ; avec sqlc en plus, cela devient GoTHs ou GHosT Accepter go generate ./... comme étape de build apporte beaucoup, et goverter sert aussi à générer les conversions entre les modèles sqlc, les objets de templates et les valeurs de retour Environ 50 % du boilerplate est généré automatiquement, ce qui donne une très bonne sûreté de typage
    • Utilise beaucoup jet récemment pour le SQL type-safe La génération de code nécessite une base de données en cours d’exécution, mais le fait que cela force à appliquer les migrations avant d’écrire le code est aussi vu comme un avantage https://github.com/go-jet/jet
    • Pour de petites apps web réactives, comme des tableaux de bord d’API en temps réel qui peuvent même ne pas avoir besoin de leur propre base de données, utilise souvent récemment le template PAHG C’est une combinaison de Pico.css, Alpine.js, HTMX et Go, à laquelle on peut bien sûr connecter SQLite https://github.com/hiAndrewQuinn/pahg-template
  • HTMX est excellent pour de nombreux usages, mais il est très difficile à introduire si les collègues de l’équipe ne sont pas d’accord Les réactions disant que ce n’est pas une technologie sérieuse persistent, toutes sortes de bugs sont d’abord attribués à HTMX, et même lorsqu’il s’avère plus tard que c’était une méprise, la confiance est déjà entamée C’est arrivé même dans la meilleure équipe avec laquelle la personne ait travaillé jusqu’ici, et la leçon retenue est qu’il faut choisir ses combats Considère aussi que html/template de Go a une interface particulièrement peu naturelle. Comme le recommande 《A Philosophy of Software Design》, il faudrait repousser la complexité vers l’intérieur ; or une structure qui oblige à se soucier de cloner les templates à chaque rendu HTML est inutilement complexe

    • Aime aussi HTMX, mais a vécu la même réalité lors d’une tentative d’adoption en équipe L’a proposé pour une app web simple, mais un développeur habitué depuis des années à renvoyer du JSON et à faire le rendu avec JS/jQuery a eu du mal à résoudre les mêmes problèmes avec cette nouvelle approche à chaque étape, un autre développeur n’a même pas essayé, et l’équipe continuait à ne parler que de Blazor N’importe quelle nouvelle approche aurait probablement été difficile, mais HTMX n’a en pratique jamais eu sa chance C’est dommage : dans une petite équipe, éviter 95 % du JavaScript et générer le code et le HTML côté serveur rendrait la base de code plus propre et les tests plus faciles. Sur les projets personnels, continue à utiliser HTMX, a trouvé un bon rythme et estime que le développement web s’en trouve grandement simplifié
    • Dans un rôle de conseil, a activement accompagné l’équipe en expliquant qu’apprendre HTMX approfondit la compréhension du navigateur, qui est l’environnement réel de travail, tandis que React et d’autres isolent les développeurs du navigateur, mais a vécu la même chose : https://www.reddit.com/r/htmx/s/DuXyGgsCWK html/template est appréciable pour sa sécurité par défaut et son intégration à la bibliothèque standard, mais son caractère dynamique laisse trop de zones sans réponse claire ; après avoir écrit plusieurs grandes applications commerciales avec Templ, l’expérience de développement a aussi semblé décevante C’est pourquoi gsx a été créé : https://github.com/gsxhq/gsx
    • Il n’est pas nécessaire de cloner les templates à chaque rendu ; il suffit de les compiler une fois puis de les exécuter Le problème de l’article d’origine est auto-infligé, car il essaie de définir le titre de la page depuis le template principal. Il suffit de passer le titre via le contexte et de découper base.tmpl en BASE_BEGIN et BASE_END, puis de les utiliser dans le template final Le titre propre à chaque page doit être transmis à l’exécution, mais la méthode de l’article d’origine finira de toute façon par casser dès que le support multilingue commencera
    • Dans notre entreprise, c’est exactement l’inverse : malgré toutes sortes de problèmes, il est impossible de convaincre que la SPA React ne passe pas à l’échelle, parce que c’est une technologie établie Chaque problème est examiné séparément, sans parvenir à les relier à la vue d’ensemble montrant qu’ils proviennent tous de la même structure
  • A pris beaucoup de plaisir à utiliser HTMX dans un projet récent Pour quelqu’un qui connaît le Web d’avant AngularJS et React, le fait de pouvoir construire de vraies pages et de réduire au minimum la quantité de JavaScript est particulièrement appréciable C’est possible en JavaScript pur aussi, mais HTMX évite le boilerplate répétitif de gestion des événements Si la philosophie du frontend moderne ne vous plaît pas, cela vaut la peine d’essayer ; les premiers exemples du site officiel sont très basiques, mais avec un peu plus d’apprentissage, l’outil devient bien plus puissant

    • Recommanderait aussi prudemment SvelteKit De l’extérieur, cela ressemble à un framework frontend JavaScript de plus, mais après l’avoir utilisé directement, il prend très bien en charge la conception centrée sur l’hypertexte recherchée par l’écosystème HTMX
  • J’ai essayé de créer un projet assez conséquent avec HTMX + Go, mais ce n’était pas encore suffisant, et je ne suis pas sûr que cela atteigne un jour le niveau que je souhaite. C’est excellent pour les applis CRUD simples et les tableaux de bord d’administration, mais dès qu’il y a beaucoup de composants interconnectés, d’état partagé et d’interactions complexes, cela devient vite difficile à gérer. J’avais choisi HTMX parce que je n’aime pas React, mais j’ai finalement gardé Go pour le backend et basculé le frontend en mode SPA avec SvelteKit. En séparant proprement les deux, j’ai pu développer et maintenir des interfaces complexes beaucoup plus facilement. Svelte ressemble davantage à une extension naturelle du HTML qu’à un autre langage comme JSX, son modèle de gestion d’état et de composants est simple, et la nouvelle syntaxe $state est particulièrement agréable.

    • Le site de HTMX expose aussi clairement ce compromis, en indiquant qu’il n’est pas adapté à ce type d’usage, mais il faut creuser un peu pour trouver les articles à ce sujet.
    • Hyperscript, le projet frère frontend de l’équipe HTMX, gère des fonctionnalités comme l’état côté frontend. Je me demande si tu l’as aussi essayé : https://hyperscript.org/docs/reactivity/
    • Il me semble que le cœur de la philosophie HTMX consiste justement à se demander si l’on a vraiment besoin de composants reliés par un état partagé. Au final, la plupart des applications ne font que du CRUD sur une base de données.
  • Ces temps-ci, j’écris presque tous mes side projects en Go + HTMX, avec ou sans l’aide des LLM. Opus et GPT maîtrisent aussi très bien cette combinaison ; on peut construire et démarrer rapidement, et comme c’est un binaire unique, le déploiement et l’hébergement sont pratiques. C’est une très bonne stack pour itérer rapidement.

  • Si vous utilisez HTMX, je recommande vivement une approche de génération HTML qui permette, côté backend aussi, d’extraire facilement des fragments HTML communs sous forme de fonctions, comme des composants à la React. Avec HTMX, selon le contexte, il faut de la flexibilité dans le HTML généré par le backend, par exemple ajouter ou retirer des balises juste à côté d’un HTML donné. Par exemple, s’il trouve une balise `` au niveau supérieur, il met à jour le titre de la page. Avec les moteurs de templates classiques basés sur des chaînes, ce genre de chose est difficile, mais avec une bibliothèque HTML intégrée au langage, c’est simple, et l’on peut utiliser toutes les capacités d’abstraction du langage, ce qui se marie particulièrement bien avec HTMX. Pour un backend JavaScript, on peut utiliser une fonction de tagged template literal comme https://github.com/WebReflection/uhtml-ssr ; en Go, https://www.gomponents.com/ ; en Scala, ScalaTags.

  • J’ai découvert Go avec le livre d’Alex Edwards et Learn Go with Tests, et je les recommande encore aujourd’hui. Cela me donne envie d’essayer de convertir certaines parties de vieux projets à HTMX.

    • Si le développement web en Go vous intéresse ne serait-ce qu’un peu, vous devriez lire Let's Go et Let's Go Further d’Alex Edwards. Ce sont parmi les livres d’introduction à un langage de programmation les plus accessibles, agréables et pratiques que j’aie lus.
  • J’ai créé pour mon usage personnel un framework basé sur Kotlin + HTMX : https://github.com/reubenfirmin/zoned L’objectif était de vérifier s’il était possible de rendre toute une application web typée de bout en bout, et j’utilise Kotlinx.html pour écrire le HTML avec un DSL proche de JSX. J’ai fait moi-même les 90 premiers pour cent, puis j’ai demandé l’aide de Claude pour finaliser les 10 derniers pour cent et le README en vue de la publication.

  • J’aime Go comme Alex Edwards, mais mes résultats avec HTMX ont toujours été décevants. J’ai l’impression que la complexité du codebase augmente deux fois plus vite que celle de l’application elle-même, et je finis toujours par tomber sur des cas limites dont on ne sort pas sans bricolages étranges. Je comprends pourquoi on peut détester les paquets Node, mais HTMX me semble être une surcompensation. Le temps gagné à ne pas se battre avec JSON est plus que triplé par le temps nécessaire pour obtenir une appli qui ait vraiment bonne apparence et un bon ressenti. Un template Mantine se configure en deux minutes et donne accès à d’excellents composants d’UI ; en incluant les assets statiques générés, on peut tout aussi bien en faire un binaire Go unique : https://github.com/mantinedev/vite-min-template

    • Quand j’ai découvert l’hypermédia avec Rails, c’était pratique et simple, donc les frictions avec Go/HTMX m’ont surpris. Mais après plusieurs tentatives, j’ai appris que les abstractions comptent. Sans générateur de composants ni routeur inverse, cela devient assez pénible, alors que Rails fournit tout cela par défaut, si bien qu’on n’a pas à s’en soucier. Le plus gros avantage est que les calculs et les données se trouvent au même endroit. Les LLM utilisent et testent aussi très bien les applications hypermédia autonomes, et comme il n’y a pas à attendre JavaScript, les cycles d’itération sont plus courts. Cela dit, je suis d’accord sur le fait que l’absence de bibliothèque de composants prête à l’emploi rend le démarrage difficile.
    • Pour utiliser HTMX, j’ai dû changer toute ma philosophie de conception. Ce n’est pas forcément un inconvénient, cela peut même être une bonne évolution, mais c’est un vrai basculement, et le résultat ressemble davantage au web dans ce qu’il a de propre qu’à une imitation d’application mobile.
    • Je serais curieux d’avoir un exemple concret d’UI que tu pouvais construire avec Mantine mais pas avec HTMX.
  • Je préfère largement Go + Datastar, parce que c’est plus simple.

    • Go et Datastar vont ensemble comme un gant. Pour les templates, j’aimerais ajouter a-h/templ, que Datastar prend aussi en charge. Je suis surpris que Datastar soit aussi peu connu.
    • Je préfère moi aussi Datastar, mais les nouvelles fonctionnalités de HTMX4 réduisent beaucoup l’écart. C’est réjouissant de voir l’écosystème converger vers de bons patterns.