# آلبين.js و HTMX 'Parameter of Clear Query StringMSC2sM SK3 tag helper for ASP .NET Core

<datetime class="hidden">2025-04-25T23:00</datetime>

<!--category-- Javascript, HTMX, Alpine.js, ASP.NET Core -->
# المقدمة

فقط واحدة سريعة, كان لدي حاجة في مشروع عمل للقدرة على 'clear' параметрات URL من URLM SK3 هذا مفيد عندما يكون لديك URL مع العديد من العواملMSC4 وتريد أن تزيل واحد أو أكثر منها ♫( على سبيل المثال لصفر البحث ♫ ).

[TOC]

# المشكلة

مشروعي الحالي يستخدم عبارة عن سطر بحث مدرسي قديمة.

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

```

الآن يمكن لهذه أن تتغير مع كل صفحة , بحيث يمكنني أن أحصل على حفرة في موقع الصفحة URL وأحتاج إلى أن أكون قادراً على التخلص منها دون كتابة مجموعة من لوحة المحرك للقيام بذلك

يمكنك أن تفعل هذا كجزء من أي تحكم إدخال تستخدم لذلك على سبيل المثال بالقرب من كل مربع للتأكد ( أو بأسلوب وضعية مبهجة ل រូបតំណាង واضح ) ولكن ويمكنك استخدام هذه التقنية لتلك أيضاً. ومع ذلكM SK3 في هذه الحالة أردت أن أفعل شيئين رئيسيينMSC4

1. سيكون بمقدورك إزالة متغير إسمه
2. قادرة على إزالة قائمة من المتغيرات.
3. وتكون قادرة على إزالة كل العوامل
4. أجعلها تُرسل مع HTMX
5. هل يمكن استخدام مؤشر تحميلي؟ [إشارة شحني](https://www.mostlylucid.net/blog/usingsweetalertforhxindicators).

# الحل

في مشروعي أستخدم

- HTMX
- Alpine.js
- ASP.NET Core
- TailwindCSS
- دييزيو

لذا كان حلي يركز حول استخدام هذه للحصول على تصميم جميل

## مُساعد العلامة

مُساعدي على TagHelper هو بسيط جدا, كل ما أفعله هو إنشاء `<a>` tag with a few attributes I'll later pass into the Alpine Module and weM SK1re done.

```csharp
[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>");
    }
}
```

### العوامل

في استعمال هذا يبدو مثل هذا, أولاً 'clear all parametersM SK2 لذا أنا فقط أنظر إلى `Context.Request.Query` إذا كان هناك ' أي پارامترات هناك أحول القليل `x` الأيقون الذي يسمح للمستخدم بإزالة كل العوامل

```html

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

```

وبدلاً من ذلك يمكنني القيام بهذا

```html

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

والذي يوضح بالطبع أن المتغير الوحيد

أو حتى

```html

<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>
```

هذا يزيل بعد ذلك كل المتغيرات المسمولة من السلسلة.

### الـ `target` السمة

لديكم أيضًا الخيار للدخول في `target` السمة التي ستستخدم ك `hx-target` attribute. هذا مفيد إذا كنت تريد تحديث جزء معين من الصفحة مع المحتوى الجديد.

```html

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

في حالتي (لأنني كتبته) لقد قمت بتحديد الهدف إلى هدفي `#page-content` div.

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

```

### النتيجة

و هذا ينتج عن رسم HTML :

- كلها:
  إذن نحصل على HTML مع `x-all` attribute and no `x-param` atribute.

```html
<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>

```

- واحد
  نحصل على HTML مع `x-param` attribute and no `x-all` atribute.

```html
<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>
```

- متعددة
  نحصل على HTML مع `x-param` attribute with a comma separated string and no `x-all` atribute.

```html
<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>
```

كل واحد منهم لديه أيضاً الخصائص الجبلية `x-data` و `x-on:click.prevent` التي تستخدم لإعداد نموذج الألبين وتدعو الالة لتزيل المتغيرات.

ن'سنرى كيف يعمل ذلك بعد ذلك...

## وحدة الألبين

هذا بالطبع ممكن من خلال استخدام Alpine.js لتنظيم طلبنا وHTMX لتنفيذه

كما ترون في الرمز من الأسفل, لدي نموذج بسيط يأخذ `path` من الصفحة الحالية ومن ثم يستخدم `URL` API لتحليل سطر الطلب ( يمكنك أيضاً المرور في سبب مختلف

ثم نحصل على العنصر الذي تم تصفحه ونتأكد إن كان لديه `x-all` attribute; إذا أمكننا حذف جميع العوامل من URL, وإلا فسنقسم `x-param` attribute by commas and delete each of those parameters.

ثم نخلق موقعاً جديداً بسلسلة الសំណួរ المحدثة ونستخدم HTMX لطلب ذلك الموقع

```javascript
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;
```

### الـ `showAlert` Function using SweetAlert2

سوف تلاحظون أيضاً أنني أدعو `showAlert` Function. هذا مجرد غلاف بسيط حول SweetAlert

هذا تم تعديله قليلا من [في المرة الأخيرة التي رأيناها](https://www.mostlylucid.net/blog/usingsweetalertforhxindicators). لذا يمكنني استخراج `showAlert` Function and make it available to external modules. Which let's me use it in both the `param-clearer` وحدة و `hx-indicator` وحدة تحكم

```javascript
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;
```

كتذكرة يستخدم هذا `path` كمفتاح لمعرفة متى يخفي التحذير.

### HTMX

أخيرا, نستخدم `htmx.ajax` لصنع الطلب. هذا طلب GET بسيط للURL الجديد الذي قمنا بصنعه مع سلسلة الសំណួរ المحدثة.

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

# في الختام

هذه طريقة بسيطة لإزالة پارامترات الURL باستخدام مساعد البطاقات و آلبين