Un assistant de balise Alpine.js et HTMX activé pour ASP.NET Core (Français (French))

Un assistant de balise Alpine.js et HTMX activé pour ASP.NET Core

Comments

NOTE: Apart from English (and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.

Wednesday, 23 April 2025

//

Less than a minute

Présentation

Juste un rapide, j'avais un besoin dans un projet de travail pour la capacité de « effacer » les paramètres d'URL d'une URL. Ceci est utile lorsque vous avez une URL avec plusieurs paramètres, et vous voulez supprimer un ou plusieurs d'entre eux (par exemple pour un filtre de recherche).

Le problème

Mon projet actuel utilise des chaînes de requêtes d'anciennes écoles (c'est un site d'administration donc n'a pas besoin de la fantaisie des urls 'bien'). Donc je me retrouve avec une URL comme celle-ci:

/products?category=electronics&search=wireless+headphones&sort=price_desc&inStock=true&page=3

Maintenant, ils peuvent varier avec chaque page, de sorte que je peux finir avec un BUNCH dans l'URL de la page et je dois être en mesure de les effacer sans écrire un tas de plaque de chaudière pour le faire.

Vous pouvez le faire dans le cadre de tout contrôle d'entrée que vous utilisez ainsi par exemple à côté de chaque case à cocher (ou une icône claire de style de placeholder fantaisie) mais vous pouvez utiliser cette technique pour ceux aussi. Cependant, dans ce cas, je voulais faire deux choses principales:

  1. Être en mesure d'effacer un paramètre nommé
  2. Être en mesure d'effacer une liste de paramètres.
  3. Être en mesure d'effacer tous les paramètres
  4. Remettez-le en ligne avec HTMX
  5. Faites-le utiliser mon indicateur de chargement mon indicateur de chargement.

La solution

Dans mon projet, j'utilise déjà

  • HTMX
  • Alpine.js
  • ASP.NET Core
  • TaiwindCSS
  • DaisyUI

Ma solution était donc concentrée autour de l'utilisation de ceux-ci pour obtenir une solution agréable et fonctionnelle avec un code minimal.

L'aide à l'étiquette

Mon TagHelper est assez simple, tout ce que je fais est de créer un <a> tag avec quelques attributs que je passerai plus tard dans le module alpin et nous avons terminé.

[HtmlTargetElement("clear-param")]
public class ClearParamTagHelper : TagHelper
{
    [HtmlAttributeName("name")]
    public string Name { get; set; }
    
    [HtmlAttributeName("all")]
    public bool All { get; set; }= false;
    
    [HtmlAttributeName("target")]
    public string Target { get; set; } = "#page-content";

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "a";
        output.Attributes.SetAttribute("x-data", "window.queryParamClearer({})");

        if (All)
        {
        output.Attributes.SetAttribute("x-all", All);
        }
        else
        {
            output.Attributes.SetAttribute("x-param", Name);
        }
        output.Attributes.SetAttribute("data-target", Target);
        output.Attributes.SetAttribute("x-on:click.prevent", "clearParam($event)");
        output.Content.SetHtmlContent(@"
            <div class='w-6 h-6 flex items-center justify-center bg-red-600 text-white rounded-full'>
                <i class='bx bx-x text-lg'></i>
            </div>");
    }
}

Paramètres

Dans l'utilisation ceci ressemble à ceci, d'abord pour 'clairer tous les paramètres'. Donc, je regarde juste le Context.Request.Query s'il y a des paramètres là-bas, je rends le petit x icône pour laisser l'utilisateur effacer tous les paramètres.


@if(Context.Request.Query.Any())
{
<label class="param-label">
    <clear-param all="true"></clear-param>
    clear all
</label>
}
</div>

Alternativement pour les paramètres nommés, je peux le faire


<div class="param-label">
    <clear-param name="myParam"></clear-param>
    <p>My Param: @Model.MyParam</p>
</div>

Ce qui effacerait bien sûr ce paramètre unique.

Ou même


<div class="param-label">
    <clear-param name="myParam1,myParam2,myParam3"></clear-param>
    <p>My Param: @Model.MyParam1</p>
    <p>My Param: @Model.MyParam2</p>
    <p>My Param: @Model.MyParam3</p>
</div>

Cela efface alors tous les paramètres nommés de la chaîne.

Les target attribut

You ont également la possibilité de passer dans un target l'attribut qui sera utilisé comme l'attribut hx-target attribut. Ceci est utile si vous voulez mettre à jour une partie spécifique de la page avec le nouveau contenu.


<div class="param-label">
    <clear-param name="myParam" target="#my-thing"></clear-param>
    <p>My Param: @Model.MyParam</p>
</div>

Dans mon cas (parce que je l'ai écrit) j'ai par défaut la cible à mon #page-content Div.

    [HtmlAttributeName("target")]
    public string Target { get; set; } = "#page-content";

Le résultat

Ces résultats donnent lieu au rendu du HTML suivant :

  • Tous : Donc nous obtenons HTML avec le x-all attribut et non x-param attribut.
<a x-data="window.queryParamClearer({})" x-all="True" data-target="#page-content" x-on:click.prevent="clearParam($event)">
    <div class="w-6 h-6 flex items-center justify-center bg-red-600 text-white rounded-full">
        <i class="bx bx-x text-lg"></i>
    </div>
</a>

  • Célibataire Nous obtenons HTML avec le x-param attribut et non x-all attribut.
<a x-data="window.queryParamClearer({})" x-param="myParam" data-target="#page-content" x-on:click.prevent="clearParam($event)">
    <div class="w-6 h-6 flex items-center justify-center bg-red-600 text-white rounded-full">
        <i class="bx bx-x text-lg"></i>
    </div>
</a>
  • Nombreux Nous obtenons HTML avec le x-param attribut avec une chaîne séparée par virgule et non x-all attribut.
<a x-data="window.queryParamClearer({})" x-param="myParam1,myParam2,myParam3" data-target="#page-content" x-on:click.prevent="clearParam($event)">
    <div class="w-6 h-6 flex items-center justify-center bg-red-600 text-white rounded-full">
        <i class="bx bx-x text-lg"></i>
    </div>
</a>

Chacun d'eux a aussi les deux attributs alpins x-data et x-on:click.prevent qui sont utilisés pour configurer le module Alpine et appeler la fonction pour effacer les paramètres.

On va voir comment ça marche.

Le module alpin

Ceci est bien sûr possible grâce à Alpine.js pour configurer notre demande et HTMX pour l'exécuter.

Comme vous pouvez le voir dans le code ci-dessous, j'ai un module simple qui prend le path de la page actuelle, puis utilise URL API pour analyser la chaîne de requête (vous pouvez également passer dans une autre pour quelque raison que ce soit :)).

On obtient alors l'élément qui a été cliqué et on vérifie s'il a le x-all attribut; si nous supprimons tous les paramètres de l'URL, sinon nous avons divisé le x-param attribut par virgules et supprimer chacun de ces paramètres.

Ensuite, nous créons une nouvelle URL avec la chaîne de requête mise à jour et utilisons HTMX pour faire une requête à cette URL.

export function queryParamClearer({ path = window.location.pathname }) {
    return {
        clearParam(e) {
            const el = e.target.closest('[x-param],[x-all]');
            if (!el) return;

            const url = new URL(window.location.href);

            if (el.hasAttribute('x-all')) {
                // → delete every single param
                // we copy the keys first because deleting while iterating modifies the collection
                Array.from(url.searchParams.keys())
                    .forEach(key => url.searchParams.delete(key));
            } else {
                // → delete only the named params
                (el.getAttribute('x-param') || '')
                    .split(',')
                    .map(p => p.trim())
                    .filter(Boolean)
                    .forEach(key => url.searchParams.delete(key));
            }

            const qs = url.searchParams.toString();
            const newUrl = path + (qs ? `?${qs}` : '');

            showAlert(newUrl);
            htmx.ajax('GET', newUrl, {
                target: el.dataset.target || el.getAttribute('hx-target') || 'body',
                swap: 'innerHTML',
                pushUrl: true
            });
        }
    };
}

//In your entry point / anywhere you want to register the module
import { queryParamClearer } from './param-clearer.js'; // webpackInclude: true

window.queryParamClearer = queryParamClearer;

Les showAlert fonction utilisant SweetAlert2

Vous noterez aussi que j'appelle showAlert fonction. Ce n'est qu'un simple emballage autour de l'indicateur de chargement SweetAlert2 que j'utilise dans mon projet. Vous pouvez bien sûr remplacer cela par ce que vous voulez faire.»

Il s'agit là d'un phénomène légèrement modifié par rapport à l'année précédente. la dernière fois qu'on l'a vuC'est ce que j'ai dit. Pour que je puisse extraire le showAlert de fonctionner et de le mettre à la disposition des modules externes. Ce qui me permet de l'utiliser dans les deux param-clearer le module et le hx-indicator module.

export function registerSweetAlertHxIndicator() {
    document.body.addEventListener('htmx:configRequest', function (evt) {
        const trigger = evt.detail.elt;

        const indicatorAttrSource = getIndicatorSource(trigger);
        if (!indicatorAttrSource) return;

        // ✅ If this is a pageSize-triggered request, use our custom path
        let path;
        if (evt.detail.headers?.['HX-Trigger-Name'] === 'pageSize') {
            path = getPathWithPageSize(evt.detail);
            console.debug('[SweetAlert] Using custom path with updated pageSize:', path);
        } else {
            path = getRequestPath(evt.detail);
        }

        if (!path) return;
        evt.detail.indicator = null;
        showAlert(path);
    });
}

export function showAlert(path)
{
    const currentPath = sessionStorage.getItem(SWEETALERT_PATH_KEY);

    // Show SweetAlert only if the current request path differs from the previous one
    if (currentPath !== path) {
        closeSweetAlertLoader();
        sessionStorage.setItem(SWEETALERT_PATH_KEY, path);


        Swal.fire({
            title: 'Loading...',
            allowOutsideClick: false,
            allowEscapeKey: false,
            showConfirmButton: false,
            theme: 'dark',
            didOpen: () => {
                // Cancel immediately if restored from browser history
                if (sessionStorage.getItem(SWEETALERT_HISTORY_RESTORED_KEY) === 'true') {
                    sessionStorage.removeItem(SWEETALERT_HISTORY_RESTORED_KEY);
                    Swal.close();
                    return;
                }

                Swal.showLoading();
                document.dispatchEvent(new CustomEvent('sweetalert:opened'));

                // Set timeout to auto-close if something hangs
                clearTimeout(swalTimeoutHandle);
                swalTimeoutHandle = setTimeout(() => {
                    if (Swal.isVisible()) {
                        console.warn('SweetAlert loading modal closed after timeout.');
                        closeSweetAlertLoader();
                    }
                }, SWEETALERT_TIMEOUT_MS);
            },
            didClose: () => {
                document.dispatchEvent(new CustomEvent('sweetalert:closed'));
                sessionStorage.removeItem(SWEETALERT_PATH_KEY);
                clearTimeout(swalTimeoutHandle);
                swalTimeoutHandle = null;
            }
        });
    }
}

//Register it
import { registerSweetAlertHxIndicator, showAlert } from './hx-sweetalert-indicator.js';
registerSweetAlertHxIndicator();
window.showAlert = showAlert;

Comme un rappel, il utilise le path comme la clé pour savoir quand cacher l'alerte.

HTMX

Enfin, nous utilisons htmx.ajax de faire la demande. Il s'agit d'une simple requête GET à la nouvelle URL que nous avons créée avec la chaîne de requête mise à jour.

   htmx.ajax('GET', newUrl, {
                target: el.dataset.target || el.getAttribute('hx-target') || 'body',
                swap: 'innerHTML',
                pushUrl: true
            });

En conclusion

Il s'agit d'une façon simple de effacer les paramètres d'URL à l'aide d'une balise helper et Alpine.js. Il vous permet d'effacer tous les paramètres, ou seulement ceux spécifiques, avec un code minimal.

logo

©2024 Scott Galloway