This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
HTMX est une puissante bibliothèque JavaScript qui vous permet de créer des applications web dynamiques avec un minimum de JavaScript. Il vous permet de faire des requêtes AJAX, d'échanger du contenu HTML et de gérer les événements directement dans vos attributs HTML. J'utilise HTMX depuis environ deux ans à ce stade et avec chaque projet j'en apprends de plus en plus sur ses capacités; et surtout, c'est des limites.
Cependant, je ne prétends toujours pas en avoir une connaissance experte. Je voulais juste partager certaines choses que j'ai apprises en chemin.
Afin de permettre l'extensibilité, HTMX soulève un certain nombre d'événements au cours de son cycle de vie. Ces événements peuvent être utilisés pour s'accrocher au cycle de vie HTMX et effectuer des actions personnalisées à différentes étapes. En fonction de l'étape, vous pouvez modifier les urls, les en-têtes, les paramètres de requête en modifiant le contenu avant / après l'échange HTMX, etc.
La phase de préparation de la demande est l'endroit où HTMX met en place la demande avant sa sortie. Cela comprend la personnalisation des en-têtes, la gestion de la confirmation de l'utilisateur et la collecte des entrées. Les événements suivants permettent un contrôle fin de ces comportements :
htmx:configRequest
– Viré avant l'envoi de la demande. Vous permet de modifier les en-têtes, les paramètres ou l'URL de la requête.htmx:confirm
– Viré pour autoriser les dialogues de confirmation. Annuler l'événement si l'utilisateur décline.htmx:prompt
– Viré quand un hx-prompt
l'attribut est présent. Vous pouvez passer outre le comportement rapide ici.htmx:abort
– Viré avant l'envoi de la demande. En cas d'annulation, la demande est entièrement annulée.Une fois la requête configurée, HTMX l'envoie au serveur. Cette phase comprend l'envoi, le suivi et le traitement des réponses au niveau du réseau :
htmx:beforeRequest
– Viré juste avant l'envoi de la demande. Utile pour l'enregistrement ou les ajustements finaux.htmx:request
– Viré immédiatement après l'envoi de la demande.htmx:afterRequest
– Viré à la fin de la demande (avec succès ou avec erreur), avant le traitement de la réponse.Après la réponse du serveur, HTMX gère la façon dont la réponse met à jour le DOM. Ces événements vous permettent d'accéder aux étapes de manipulation DOM:
htmx:beforeOnLoad
– Viré avant le début du traitement de la réponse.htmx:onLoad
– Viré une fois que la réponse a chargé et est prêt pour les mises à jour DOM.htmx:beforeSwap
– Viré juste avant que le contenu soit échangé dans le DOM. Vous pouvez annuler ou personnaliser l'échange.htmx:swap
– Viré lorsque l'échange réel de DOM se produit.htmx:afterSwap
– Viré après l'échange de contenu.htmx:afterSettle
– Viré une fois les animations et les transitions terminées.htmx:afterOnLoad
– Tiré à la fin du cycle de vie de la réponse après que tout soit réglé.La phase de gestion de l'historique est l'endroit où HTMX met à jour l'historique du navigateur et l'URL. Les événements suivants sont déclenchés pendant cette phase:
htmx:historyRestoreRequest
– Viré lorsque HTMX détecte une requête pour restaurer un état de page précédent.htmx:historyPopped
– Viré lorsque l'utilisateur navigue à l'aide des boutons arrière/avant du navigateur.htmx:historyRestorePage
– Viré lorsque HTMX restaure une page précédente du contenu de l'historique.HTMX fournit un ensemble riche d'événements de cycle de vie qui vous permettent de personnaliser en profondeur comment les demandes et les réponses sont traitées, ce qui le rend remarquablement puissant pour une bibliothèque aussi légère.
L'un des aspects les plus puissants de HTMX est la capacité de créer des extensions d'étendre ses capacités. Dans mon cas, j'ai tendance à m'accrocher à htmx:configRequest
ajouter des paramètres supplémentaires à la demande. Ceci est utile lorsque vous souhaitez transmettre des données supplémentaires au serveur sans avoir à modifier le code HTML ou JavaScript.
D'autres extensions pourraient s'accrocher htmx:beforeRequest
modifier la requête avant qu'elle ne soit envoyée; mais après la plupart des autres extensions qui crochetent configRequest
; comme dans beforeRequest
des trucs comme HX-Vals
et HX-Include
s sont déjà attachés à la reuest (soit dans la charge utile \ querystring).
Tu peux même t'accrocher htmx:afterSwap
pour effectuer des actions après que le contenu a été échangé. Combiné avec le côté client templating bibliothèques comme Alpine.js ou Lit vous pouvez créer des applications dynamiques puissantes avec un code minimal.
HTMX fournit quelques extensions intégrées comme hx-boost
et hx-swap-oob
qui vous permettent d'améliorer la fonctionnalité de HTMX sans écrire de code personnalisé. Cependant, il y a des moments où vous devez créer vos propres extensions pour répondre à des exigences spécifiques.
Par exemple, vous pouvez ajouter des en-têtes personnalisés à vos demandes, modifier la charge utile de la demande ou gérer des événements spécifiques d'une manière unique.
Pour réaliser ce HTMX vous offre quelques points d'intégration pratiques:
{
/**
* init(api)
* Called once when the extension is initialized.
* Use it to set up internal state, store references, or access HTMX utility functions via the api parameter.
*/
init: function(api) {
return null;
},
/**
* getSelectors()
* Returns additional CSS selectors that HTMX should monitor.
* Useful if your extension needs to handle custom elements or dynamic behavior.
*/
getSelectors: function() {
return null;
},
/**
* onEvent(name, evt)
* Called on every HTMX event (e.g., htmx:beforeRequest, htmx:afterSwap).
* Return false to cancel the event or stop propagation.
*/
onEvent: function(name, evt) {
return true;
},
/**
* transformResponse(text, xhr, elt)
* Modify the raw response text before it is parsed and swapped into the DOM.
* Use this to sanitize or preprocess HTML.
*/
transformResponse: function(text, xhr, elt) {
return text;
},
/**
* isInlineSwap(swapStyle)
* Return true if your extension will handle this swap style manually.
* This tells HTMX to skip default behavior.
*/
isInlineSwap: function(swapStyle) {
return false;
},
/**
* handleSwap(swapStyle, target, fragment, settleInfo)
* Perform custom DOM manipulation if you implement a custom swap style.
* Return true to prevent HTMX's default swap.
*/
handleSwap: function(swapStyle, target, fragment, settleInfo) {
return false;
},
/**
* encodeParameters(xhr, parameters, elt)
* Modify or serialize request parameters before sending.
* Return null to use default URL/form encoding.
* Return a string to override with a custom payload (e.g., JSON).
*/
encodeParameters: function(xhr, parameters, elt) {
return null;
}
}
HTMX fournit un certain nombre d'extensions pré-construites que vous pouvez lire à propos de ici.
Par exemple, une pratique Extension intégrée json-encode
vous permet d'envoyer des données JSON dans le corps de la requête au lieu de données de formulaire encodées par URL. Ceci est utile lorsque vous souhaitez envoyer des structures ou des tableaux de données complexes au serveur.
Vous pouvez voir que cela s'accroche à 3 événements
init
- pour configurer l'extension et stocker une référence à l'API HTMXonEvent
- pour régler le Content-Type
en-tête vers application/json
lorsque la requête est configuréeencodeParameters
- pour surcharger le formulaire par défaut encodé par URL et sérialiser les paramètres comme JSON. Il retourne également une chaîne pour empêcher HTMX d'utiliser l'encodage par défaut du formulaire encodé par URL.(function() {
let api
htmx.defineExtension('json-enc', {
init: function(apiRef) {
api = apiRef
},
onEvent: function(name, evt) {
if (name === 'htmx:configRequest') {
evt.detail.headers['Content-Type'] = 'application/json'
}
},
encodeParameters: function(xhr, parameters, elt) {
xhr.overrideMimeType('text/json')
const object = {}
parameters.forEach(function(value, key) {
if (Object.hasOwn(object, key)) {
if (!Array.isArray(object[key])) {
object[key] = [object[key]]
}
object[key].push(value)
} else {
object[key] = value
}
})
const vals = api.getExpressionVars(elt)
Object.keys(object).forEach(function(key) {
// FormData encodes values as strings, restore hx-vals/hx-vars with their initial types
object[key] = Object.hasOwn(vals, key) ? vals[key] : object[key]
})
return (JSON.stringify(object))
}
})
})()
Ou même les plus simples mais même plus maigres hx-debug
extension qui ajoute une HX-Debug
l'en-tête de la requête. Ceci est utile pour le débogage et l'enregistrement, car il vous permet de voir les données de requête brute et de réponse dans la console dev.
(function() {
htmx.defineExtension('debug', {
onEvent: function(name, evt) {
if (console.debug) {
console.debug(name, evt)
} else if (console) {
console.log('DEBUG:', name, evt)
} else {
throw new Error('NO CONSOLE SUPPORTED')
}
}
})
})()
Il y en a beaucoup d'autres, y compris un très puissant extension de templatation côté client qui vous permet d'utiliser les bibliothèques de templatation côté client pour transformer les données JSON retournées en HTML. Ceci est utile pour créer des interfaces dynamométriques sans devoir compter sur le rendu côté serveur.
Par exemple, dans un projet récent, j'ai utilisé des swaps HTMX OOB pour mettre à jour un certain nombre de lignes dans une table. Pour ce faire, je voulais savoir quelles lignes étaient actuellement affichées dans la table, donc je n'ai mis à jour que les lignes qui étaient visibles.
export default {
encodeParameters: function (xhr, parameters, elt) {
const ext = elt.getAttribute('hx-ext') || '';
if (!ext.split(',').map(e => e.trim()).includes('dynamic-rowids')) {
return null; // Use default behavior
}
const id = elt.dataset.id;
const approve = elt.dataset.approve === 'true';
const minimal = elt.dataset.minimal === 'true';
const single = elt.dataset.single === 'true';
const target = elt.dataset.target;
const payload = { id, approve, minimal, single };
if (approve && target) {
const table = document.querySelector(target);
if (table) {
const rowIds = Array.from(table.querySelectorAll('tr[id^="row-"]'))
.map(row => row.id.replace('row-', ''));
payload.rowIds = rowIds;
}
}
// Merge payload into the parameters object
Object.assign(parameters, payload);
return null; // Return null to continue with default URL-encoded form encoding
}
}
Pour l'utiliser, nous devons ajouter l'extension à notre configuration HTMX. Donc, dans votre fichier js point d'entrée (en supposant que vous utilisez des modules; yoy devrait être) vous pouvez faire quelque chose comme ceci:
import dynamicRowIds from "./dynamicRowIds"; // Load the file
htmx.defineExtension("dynamic-rowids", dynamicRowIds); // Register the extension
Ensuite, sur quel élément vous voulez l'utiliser sur vous pouvez ajouter le hx-ext
attribut avec la valeur dynamic-rowids
.
<button
hx-ext="dynamic-rowids"
data-target="#my-table"
data-id="@Model.Id"
data-param1="true"
data-param2="false"
data-param3="@Model.Whatever"
hx-post
hx-controller="Contoller"
hx-action="Action"
>
<i class='bx bx-check text-xl text-white'></i>
</button>
Il s'agit d'une autre simple extension HTMX, cette fois accrochée à htmx:configRequest
comme nous modifions l'URL avant l'envoi de la requête. Cette extension est pratique si vous utilisez le filtrage basé sur requêtestring etc. et vous voulez que certaines requêtes préservent les filtres existants tandis que d'autres ne le font pas (par exemple, 'nom' et'startdate' mais pas 'page' ou'sort').
Ceci est SIMILAR à mais pas exactement la même que l'extension HTMX existante Params poussoirs
Vous pouvez voir qu'on s'accroche onEvent
d'écouter pour htmx:configRequest
l'événement.
Ensuite, nous:
preserve-params-exclude
attribut de l'élément (s'il existe) et le diviser en un tableau de touches à exclure (pour que nous ne les ajoutions pas à la requête)export default {
onEvent: function (name, evt) {
if (name !== 'htmx:configRequest') return;
const el = evt.detail.elt;
const excludeStr = el.getAttribute('preserve-params-exclude') || '';
const exclude = excludeStr.split(',').map(s => s.trim());
const currentParams = new URLSearchParams(window.location.search);
const newParams = new URLSearchParams(evt.detail.path.split('?')[1] || '');
currentParams.forEach((value, key) => {
if (!exclude.includes(key) && !newParams.has(key)) {
newParams.set(key, value);
}
});
evt.detail.path = evt.detail.path.split('?')[0] + '?' + newParams.toString();
return true;
}
};
Ici j'utilise l'essentiel HTMX.Net pour ses aides-étiquettes. Les mêmes hx-controller
et hx-action
sont des helpers de tags qui génèrent les attributs HTMX corrects pour vous. Aussi bien que hx-route-<x>
pour les valeurs à passer dans l'itinéraire. Ceci est vraiment utile car il vous permet d'utiliser le code C# pour générer les valeurs correctes pour les attributs au lieu d'avoir à les coder dur dans votre HTML.
Être une extension c'est vraiment simple à utiliser:
Nous devons d'abord ajouter l'extension à notre configuration HTMX.
import preserveParams from './preserveParams.js';
htmx.defineExtension('preserve-params', preserveParams);
REMARQUE : Vous remarquerez que les extensions HTMX par défaut utilisent la méthode « chargement automatique » pour charger l'extension.
// Autoloading the extension and registering it
(function() {
htmx.defineExtension('debug', {
}
C'est une bonne façon de le faire si vous utilisez HTMX dans un environnement non modulaire. Cependant, si vous utilisez des modules (ce que vous devriez être), il est préférable d'utiliser le import
déclaration pour charger l'extension puis l'enregistrer explicitement contre votre htmx
C'est le cas. Cela vous permet de profiter de l'arborescence et de charger uniquement les extensions dont vous avez besoin.
Ensuite, sur votre élément, vous pouvez ajouter le hx-ext
attribut avec la valeur preserve-params
et les preserve-params-exclude
attribut avec une liste de paramètres séparés par des virgules à exclure de la requête.
<a class="btn-outline-icon"
hx-controller="MyController"
hx-action="MyAction"
hx-route-myparam="@MyParam"
hx-push-url="true"
hx-ext="preserve-params"
preserve-params-exclude="page,sort"
hx-target="#page-content"
hx-swap="innerHTML show:top"
hx-indicator>
<i class="bx bx-search"></i>
</a>
En l'espèce, en raison de event.detail.path
avoir le nouveau myparam
valeur définie dans il remplacera celui avec notre nouvelle valeur mais préserver tous les autres (sauf page
et sort
)............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................. Ainsi, nous pouvons continuer à passer tous les filtres que nous avons définis dans l'URL au serveur sans avoir à nous soucier qu'ils soient perdus lorsque nous faisons une nouvelle requête.
L'une des choses propres à HTMX est qu'une grande partie de son interaction avec le serveur se fait par le biais d'en-têtes HTTP. Ces en-têtes fournissent au serveur un contexte riche sur ce qui a déclenché la requête, vous permettant de répondre correctement à partir de vos paramètres ASP.NET Core ou vues Razor.
Encore une fois, un élément clé de cette HTMX.NetC'est ce que j'ai dit. Parmi beaucoup d'articles qu'il fournit sont soignés Request
extensions pour détecter les demandes HTMX. Ceci est utile pour déterminer si une demande a été faite par HTMX ou non, et pour la traiter en conséquence.
Il a également son propre mécanisme pour envoyer des déclencheurs
Response.Htmx(h => {
h.WithTrigger("yes")
.WithTrigger("cool", timing: HtmxTriggerTiming.AfterSettle)
.WithTrigger("neat", new { valueForFrontEnd= 42, status= "Done!" }, timing: HtmxTriggerTiming.AfterSwap);
});
Push Urls etc...etc... Khalid a fait un excellent travail de création d'un ensemble d'extensions pour le rendre facile à travailler avec HTMX dans ASP.NET Core.
C'est un outil clé dans ma boîte à outils lorsque vous travaillez avec HTMX et ASP.NET Core. Regarde!
Voici une ventilation des en-têtes les plus utiles que HTMX envoie avec chaque requête :
Description de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête. |----------------------------|------------------------------------------------------------------------------------------------------------------| Toujours réglé à true pour toute demande d'origine HTMX. Idéal pour détecter les appels HTMX dans les intergiciels ou les contrôleurs. - Oui, c'est ça. L'identifiant de l'élément cible dans le DOM dans lequel la réponse sera échangée. - Oui, c'est ça. L'identifiant de l'élément qui a déclenché la requête (p. ex. un bouton). - Oui, c'est ça. HX-Trigger-Name Le nom de l'élément déclencheur (utile pour les formulaires). - Oui, c'est ça. HX-Prompt Contient l'entrée utilisateur de hx-prompt. - Oui, c'est ça. HX-Current-URL L'URL du navigateur lorsque la requête a été lancée. Utile pour l'enregistrement et le contexte. - Oui, c'est ça. HX-History-Restore-Request.Envoyé comme vrai si la demande fait partie d'une restauration d'historique après la navigation (p. ex., bouton arrière). - Oui, c'est ça.
Je les utilise assez largement dans mes applications ASP.NET Core. Combiné avec les HTMX.Net comme Request.IsHtmx()
, Request.IsHtmxBoosted()
et Request.IsHtmxNonBoosted()
vous pouvez facilement détecter les demandes HTMX et répondre en conséquence.
Par exemple, j'ai une extension vraiment simple à Request qui me permet de détecter si une requête vise mon principal #page-content
Div. Si c'EST alors je sais que je devrais renvoyer une partie.
REMARQUE: Beaucoup ne réalisent pas que vous pouvez spécifier une « page complète » comme une partie, il ne fait que sauter la mise en page.
if (Request.PageContentTarget())
{
Response.PushUrl(Request);
return PartialView("List", vm);
}
return View("List", vm);
public static class RequestExtensions
{
public static bool PageContentTarget(this HttpRequest request)
{
bool isPageContentTarget = request.Headers.TryGetValue("hx-target", out var pageContentHeader)
&& pageContentHeader == "page-content";
return isPageContentTarget;
}
}
En plus des extensions Request, vous pouvez également créer des extensions de réponse pour renvoyer les événements au client. Ceci est utile pour déclencher des événements côté client.
Par exemple dans mon intégration SweetAlert2 J'autorise la fermeture de la boîte de dialogue à l'aide d'un déclencheur défini à partir du serveur.
document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);
Ceci est déclenché à partir du serveur en tant qu'événement déclencheur HTMX.
public static void CloseSweetAlert(this HttpResponse response)
{
response.Headers.Append("HX-Trigger" , JsonSerializer.Serialize(new
{
sweetalert = "close"
}));
}
Cela déclenchera la sweetalert:close
événement du côté client, vous permettant de fermer la boîte de dialogue. Vous pouvez également transmettre les données au client en utilisant la HX-Trigger
l'en-tête. Ceci est utile pour transmettre les données du serveur au client sans avoir à modifier le code HTML ou JavaScript.
Comme vous le voyez, il est facile d'écouter ces événements en ajoutant simplement un auditeur d'événement au corps. J'utilise JSON principalement car il code toujours correctement.
J'ai déjà écrit sur ma méthode de toast. Ici., mais ça vaut la peine d'être mentionné ici aussi. Pour très simplement permettre au serveur de déclencher une notification toast du côté client. J'ai mis le déclencheur dans cette extension de réponse.
public static void ShowToast(this HttpResponse response, string message, bool success = true)
{
response.Headers.Append("HX-Trigger", JsonSerializer.Serialize(new
{
showToast = new
{
toast = message,
issuccess =success
}
}));
}
Je m'accroche ensuite au côté client de l'événement et j'appelle mon showToast
fonction.
import { showToast, showHTMXToast } from './toast';
window.showHTMXToast = showHTMXToast;
document.body.addEventListener("showToast", showHTMXToast);
C'est alors que j'appelle showToast
fonction et bien, montre un toast; à nouveau voir plus à ce sujet dans l'article .
export function showHTMXToast(event) {
const xhr = event?.detail?.xhr;
let type = 'success';
let message = xhr?.responseText || 'Done!';
try {
const data = xhr ? JSON.parse(xhr.responseText) : event.detail;
if (data.toast) message = data.toast;
if ('issuccess' in data) {
type = data.issuccess === false ? 'error' : 'success';
} else if (xhr?.status >= 400) {
type = 'error';
} else if (xhr?.status >= 300) {
type = 'warning';
}
} catch {
if (xhr?.status >= 400) type = 'error';
else if (xhr?.status >= 300) type = 'warning';
}
showToast(message, 3000, type);
}
C'est ça, un tour tourbillonnant de HTMX et ASP.NET Core. J'espère que vous l'avez trouvé utile et instructif. Si vous avez des questions ou des commentaires, n'hésitez pas à les commenter ci-dessous.