Back to "HMASX लोड करने के लिए स्वलवश2 प्रयोग किया जा रहा है (hx-inider)"

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 Javascript

HMASX लोड करने के लिए स्वलवश2 प्रयोग किया जा रहा है (hx-inider)

Monday, 21 April 2025

परिचय

एक कार्य परियोजना पर मैं प्रयोग कर रहा है और HMMMX एक प्रशासक यूआई का निर्माण करने के लिए. इस का हिस्सा के रूप में मैं सुंदर उपयोग कर रहा हूँ स्व- धातु2color जावा स्क्रिप्ट लाइब्रेरी मेरे पुष्टि संवाद के लिए___ यह बहुत अच्छा काम करता है लेकिन मैं भी उनका उपयोग करना चाहता था मेरे HMMX लोड दरों को बदलने के लिए.

यह एक मुश्किल साबित हो गया तो मैंने सोचा कि मैं इसे यहाँ दस्तावेज़ होगा आप को बचाने के लिए एक ही दर्द को बचाने के लिए।

Warning I'm a C# coder my Javascript is likely horrible.

[TOC]

समस्या

तो HMAX बहुत चालाक है, यह है hx-indicator सामान्य रूप से आप अपने HMMMX निवेदन के लिए एक लोड सूचक सेट करने की अनुमति देता है. श्रेणी में यह आपके पृष्ठ में एक एचटीएमएल तत्व है


<div id="loading-modal" class="modal htmx-indicator">
    <div class="modal-box flex flex-col items-center justify-center bg-base-200 border border-base-300 shadow-xl rounded-xl text-base-content dark text-center ">
        <div class="flex flex-col items-center space-y-4">
            <h2 class="text-lg font-semibold tracking-wide">Loading...</h2>
            <span class="loading loading-dots loading-xl text-4xl text-stone-200"></span>
        </div>
    </div>
</div>

तो जब आप इसे इस्तेमाल करना चाहते हैं आप अपने HMAX अनुरोध के साथ hx-indicator="#loading-modal" और जब आग्रह प्रगति में है तब यह दिखाता है किविवरण के लिए यहाँ देखें).

अब एटीएमएक्स कुछ चतुर जादू किसी का उपयोग करता है request वस्तु यह आंतरिक ट्रैक

  function addRequestIndicatorClasses(elt) {
    let indicators = /** @type Element[] */ (findAttributeTargets(elt, 'hx-indicator'))
    if (indicators == null) {
      indicators = [elt]
    }
    forEach(indicators, function(ic) {
      const internalData = getInternalData(ic)
      internalData.requestCount = (internalData.requestCount || 0) + 1
      ic.classList.add.call(ic.classList, htmx.config.requestClass)
    })
    return indicators
  }

इसलिए इन्हें रखना एक चुनौती का सा है. कैसे आप अनुरोधों को ट्रैक करते हैं और फिर स्वंयवेशनल दिखाने के लिए जब अनुरोध प्रगति में है और इसे छुपा जब यह समाप्त हो जाता है।

समाधान

तो मैं के बारे में सेट (नहीं क्योंकि मुझे करना था, क्योंकि मैं था: करने के लिए आवश्यक है:) एक स्वंशा2 नल के साथ सूचक को प्रतिस्थापित करने के लिए। वैसे भी यहाँ मैं साथ आया कोड है.

या तो आप अपने एचटीएमएल में स्व-विवेल्फ2 आयात करना प्रारंभ करेंगे (जैसे स्क्रिप्ट तथा शैली टैग) / इसे वेब बैग या समान (संग्रेज़) के लिए आयात करें (जैसे कि स्क्रिप्ट तथा शैली टैग)उनकी निगाहें झुकी होंगी

संस्थापित किये जाने के बाद आप इसे अपने जेएस फ़ाइल में इस तरह आयात कर सकते हैं.

import Swal from 'sweetalert2';

तो मेरा मुख्य कोड इस तरह दिखता है:

import Swal from 'sweetalert2';

const SWEETALERT_PATH_KEY = 'swal-active-path'; // Stores the path of the current SweetAlert
const SWEETALERT_HISTORY_RESTORED_KEY = 'swal-just-restored'; // Flag for navigation from browser history
const SWEETALERT_TIMEOUT_MS = 10000; // Timeout for automatically closing the loader

let swalTimeoutHandle = null;

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

        const path = getRequestPath(evt.detail);
        if (!path) return;

        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);
            evt.detail.indicator = null; // Disable HTMX's default indicator behavior

            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;
                }
            });
        } else {
            // Suppress HTMX indicator if the path is already being handled
            evt.detail.indicator = null;
        }
    });

    //Add events to close the loader
    document.body.addEventListener('htmx:afterRequest', maybeClose);
    document.body.addEventListener('htmx:responseError', maybeClose);
    document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);

    // Set a flag so we can suppress the loader immediately if navigating via browser history
    document.body.addEventListener('htmx:historyRestore', () => {
        sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true');
    });

    window.addEventListener('popstate', () => {
        sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true');
    });
}

// Returns the closest element with an indicator attribute
function getIndicatorSource(el) {
    return el.closest('[hx-indicator], [data-hx-indicator]');
}

// Determines the request path, including query string if appropriate
function getRequestPath(detail) {
    const responsePath =
        typeof detail?.pathInfo?.responsePath === 'string'
            ? detail.pathInfo.responsePath
            : (typeof detail?.pathInfo?.path === 'string'
                    ? detail.pathInfo.path
                    : (typeof detail?.path === 'string' ? detail.path : '')
            );

    const elt = detail.elt;

    // If not a form and has an hx-indicator, use the raw path
    if (elt.hasAttribute("hx-indicator") && elt.tagName !== "FORM") {
        return responsePath;
    }

    const isGet = (detail.verb ?? '').toUpperCase() === 'GET';
    const form = elt.closest('form');

    // Append query params for GET form submissions
    if (isGet && form) {
        const params = new URLSearchParams();

        for (const el of form.elements) {
            if (!el.name || el.disabled) continue;

            const type = el.type;
            if ((type === 'checkbox' || type === 'radio') && !el.checked) continue;
            if (type === 'submit') continue;

            params.append(el.name, el.value);
        }

        const queryString = params.toString();
        return queryString ? `${responsePath}?${queryString}` : responsePath;
    }

    return responsePath;
}

// Closes the SweetAlert loader if the path matches
function maybeClose(evt) {
    const activePath = sessionStorage.getItem(SWEETALERT_PATH_KEY);
    const path = getRequestPath(evt.detail);

    if (activePath && path && activePath === path) {
        closeSweetAlertLoader();
    }
}

// Close and clean up SweetAlert loader state
function closeSweetAlertLoader() {
    if (Swal.getPopup()) {
        Swal.close();
        document.dispatchEvent(new CustomEvent('sweetalert:closed'));
        sessionStorage.removeItem(SWEETALERT_PATH_KEY);
        clearTimeout(swalTimeoutHandle);
        swalTimeoutHandle = null;
    }
}

आप इसे विन्यस्त करें (यदि आप अपने एसएसएम का प्रयोग कर रहे हैं) main.js इस तरह का फाइल


import { registerSweetAlertHxIndicator } from './hx-sweetalert-indicator.js';
registerSweetAlertHxIndicator();

हमारे तत्वों को ढूंढा जा रहा है

आप मैं उपयोग देखेंगे getIndicatorSource उस तत्व को ढूंढने के लिए फंक्शन जो HMAX निवेदन ट्रिगर करता है. यह हमें पता करने की जरूरत है कि कौन सा तत्व शुरू होता है इसलिए हम जब यह समाप्त हो जाता है तो ऑपरेशन बंद कर सकते हैं. यह महत्वपूर्ण है जैसे HMMAX 'MAX' है, तो आप पेड़ पर चढ़ने की जरूरत है उस तत्व को खोजने के लिए जो आग्रह करता है.

function getIndicatorSource(el) {
    return el.closest('[hx-indicator], [data-hx-indicator]');
}

फिर किसी भी HMAX निवेदन पर (इसलिए) hx-get या hx-post) आप इस्तेमाल कर सकते हैं hx-indicator स्वीशACAS2 मोड निर्दिष्ट करने के लिए गुण. आपको भी क्लास को पहले की तरह निर्दिष्ट करने की जरूरत नहीं है, सिर्फ मौजूदा कार्य.

चलो के माध्यम से चलते हैं यह सब कैसे काम करता है:

इसे साथ उड़ा registerSweetAlertHxIndicator()

यह प्रविष्टि बिंदु के रूप में काम करता है. आप इसे आँकड़े में देख सकते हैं htmx:configRequest कार्यक्रम. यह निकाला जाता है जब HMMMX एक निवेदन करने के बारे में है.

तब यह तत्व हो जाता है जो घटना को जन्म देता है evt.detail.elt और जांच किया कि क्या है? hx-indicator गुण.

अंततः, यह दिखाता है कि यह स्ववश1 मोडल का उपयोग करता है Swal.fire().

rt function registerSweetAlertHxIndicator() {
    document.body.addEventListener('htmx:configRequest', function (evt) {
        const trigger = evt.detail.elt;
        const indicatorAttrSource = getIndicatorSource(trigger);
        if (!indicatorAttrSource) return;
        

निवेदन पथ पा रहा है

यदि यह होता है, तो यह निवेदन पथ हो जाता है getRequestPath(evt.detail) और उसे सत्र भंडार में जमा किया. एन. डब्ल्यू. एच. तो मैं अपने कोड में सब कुछ करते हैं. के साथ detail?.pathInfo?.path ?? detail?.path ?? '';

यह है कि HMAX भंडारित निवेदन पथ में detail.path फिर मार्ग को देखो, उसे सुगम कर दिया, document.body.addEventListener('htmx:afterRequest', maybeClose); document.body.addEventListener('htmx:responseError', maybeClose);में detail.PathInfo.responsePath तो हम दोनों को संभाल की जरूरत है.

हमें भी संभाल करने की ज़रूरत है GET फ़ॉर्म के रूप में उनकी प्रतिक्रिया के रूप में यूआरएल तत्व शामिल होंगे <input > मान लें तो अनुक्रिया URL अलग होने पर पवन चला सकता है.

// Returns the closest element with an indicator attribute
function getIndicatorSource(el) {
    return el.closest('[hx-indicator], [data-hx-indicator]');
}

// Determines the request path, including query string if appropriate
function getRequestPath(detail) {
    const responsePath =
        typeof detail?.pathInfo?.responsePath === 'string'
            ? detail.pathInfo.responsePath
            : (typeof detail?.pathInfo?.path === 'string'
                    ? detail.pathInfo.path
                    : (typeof detail?.path === 'string' ? detail.path : '')
            );

    const elt = detail.elt;

    // If not a form and has an hx-indicator, use the raw path
    if (elt.hasAttribute("hx-indicator") && elt.tagName !== "FORM") {
        return responsePath;
    }

    const isGet = (detail.verb ?? '').toUpperCase() === 'GET';
    const form = elt.closest('form');

    // Append query params for GET form submissions
    if (isGet && form) {
        const params = new URLSearchParams();

        for (const el of form.elements) {
            if (!el.name || el.disabled) continue;

            const type = el.type;
            if ((type === 'checkbox' || type === 'radio') && !el.checked) continue;
            if (type === 'submit') continue;

            params.append(el.name, el.value);
        }

        const queryString = params.toString();
        return queryString ? `${responsePath}?${queryString}` : responsePath;
    }

    return responsePath;
}

नोट: यदि आप इस्तेमाल करते हैं तो यह ख़ास तौर पर होता है HX-Push-Url निवेदन के यूआरएल को बदलने के लिए शीर्षक जो कि HMMX भंडारित करता है इतिहास के लिए.

फ़ॉर्म

HttpGet फ़ॉर्म एक छोटी सी चाल हैं तो हम कोड का एक टुकड़ा है जो पता लगाने के लिए होगा कि क्या आप क्लिक किया है submit प्रतिक्रिया यूआरएल की तुलना करने के लिए क्वैरी वाक्यांश पैरामीटरों के भीतर बटन जोड़े तथा क्वैरी पैरामीटरों को जोड़े.

const isGet = (detail.verb ?? '').toUpperCase() === 'GET';
    const form = elt.closest('form');

    // Append query params for GET form submissions
    if (isGet && form) {
        const params = new URLSearchParams();

        for (const el of form.elements) {
            if (!el.name || el.disabled) continue;

            const type = el.type;
            if ((type === 'checkbox' || type === 'radio') && !el.checked) continue;
            if (type === 'submit') continue;

            params.append(el.name, el.value);
        }

        const queryString = params.toString();
        return queryString ? `${responsePath}?${queryString}` : responsePath;
    }

    return responsePath;
    ```
    
This is important as HTMX will use the response URL to determine if the request is the same as the previous one. So we need to ensure we have the same URL in both places.

### Extensions
I use this little `Response` extension method to set the `HX-Push-Url` header in my ASP.NET Core app. I also added a second extension which will immediately close the modal (useful if you mess with the request and need to close it immediately). 
```csharp
public static class ResponseExtensions
{
    public static void PushUrl(this HttpResponse response, HttpRequest request)
    {
        response.Headers["HX-Push-Url"] = request.GetEncodedUrl();
    }
}
    public static void CloseSweetAlert(this HttpResponse response)
    {
        response.Headers.Append("HX-Trigger" , JsonSerializer.Serialize(new
        {
            sweetalert = "closed"
        }));

    }
}

यह दूसरा इलाज यहाँ लिया गया है:

    document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);

पथ का भंडारण कर रहा है

तो अब हम रास्ते है, हम इसके साथ क्या करते हैं? हम में इसे जमा करने के लिए जो अनुरोध शुरू कर दिया है के लिए खैर है कि जिस के बारे में बात करने के लिए ठीक है sessionStorage प्रयोग कर रहा है sessionStorage.setItem(SWEETALERT_PATH_KEY, path);.

( फिर भी आप इस अधिक जटिल बना सकते हैं और सुनिश्चित कर सकते हैं कि आप केवल एक ही है अगर आप की जरूरत है.)

moding दिखाने का कार्य

तो फिर हम सिर्फ स्वीजक2 मोड का उपयोग करते हुए दिखाते हैं Swal.fire()___ ध्यान दीजिए कि हम यहां विकल्पों का एक गुच्छा है.

सत्र भंडारण कुंजी के लिए चेक करने पर SWEETALERT_HISTORY_RESTORED_KEY जो इतिहास बहाल होने पर सेट किया गया है. अगर यह है, हम Elannlal तुरंत बंद करें (यह HMMMMMX हमें बचा करता है यह अजीब इतिहास प्रबंधन के साथ.

और हम ही ने (सूरज) को रौशन चिराग़ बनाया sweetalert:opened जो आप किसी भी मनपसंद तर्क करने के लिए उपयोग कर सकते हैं आप की जरूरत है.

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;
    }
});

इसके अलावा हमने ऐसे मामलों का सामना करने के लिए समय तय किया जहाँ अनुरोध लटका हुआ है । यह HMMAX के रूप में महत्वपूर्ण है हमेशा सनल बंद नहीं होता है यदि निवेदन असफल होता है (सामान्य रूप से यदि आप प्रयोग करते हैं hx-boost___ यह यहाँ सेट है const SWEETALERT_TIMEOUT_MS = 10000; // Timeout for automatically closing the loader हम कुछ गलत हो जाता है तो बंद कर सकते हैं (यह कंसोल के लिए लॉग भी करेगा).

इसे बंद किया जा रहा है

इसलिए अब हमें इसे बंद करने की ज़रूरत है जब निवेदन समाप्त हो जाता है । ऐसा करने के लिए हम कहते हैं maybeClose फंक्शन. जब निवेदन समाप्त हो जाता है तो इसे कहा जाता है (सही या त्रुटि के साथ). उपयोग में htmx:afterRequest और htmx:responseError घटनाएँ । इन घटनाओं ने एक बार HMSX ने एक निवेदन पूरा किया है (नोट, ये महत्त्वपूर्ण हैं, उदा. HX-Boost जो थोड़ा अजीब हो सकता है यह क्या हो रहा है के बारे में.)

    document.body.addEventListener('htmx:afterRequest', maybeClose);
    document.body.addEventListener('htmx:responseError', maybeClose);
    function maybeClose(evt) {
        const activePath = sessionStorage.getItem(SWEETALERT_PATH_KEY);
        const path = getRequestPath(evt.detail);

        if (activePath && path && activePath === path) {
            Swal.close();
            sessionStorage.removeItem(SWEETALERT_PATH_KEY);
        }
    }

आप इस समारोह को देखेंगे यदि सत्र भंडार में पथ वही है जो निवेदन का पथ है. यदि यह है, यह कार्यस्थान बंद करता है और सत्र भंडार से पथ हटाता है.

यह इतिहास है

HMAX के इतिहास को हैंडल करने का एक fidy तरीका है जो एक बैकअप पृष्ठ पर "स्टल' बंद कर सकता है. तो हम एक जोड़े को यह पकड़ने के लिए और अधिक घटनाएँ जोड़ते हैं (सबसे समय हमें केवल एक ही समय की ज़रूरत है).

    //Add events to close the loader
    document.body.addEventListener('htmx:afterRequest', maybeClose);
    document.body.addEventListener('htmx:responseError', maybeClose);
    document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);

    // Set a flag so we can suppress the loader immediately if navigating via browser history
    document.body.addEventListener('htmx:historyRestore', () => {
        sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true');
    });

    window.addEventListener('popstate', () => {
        sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true');
    });`

आप हम भी सेट देखेंगे sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true'); जो हम अंदर की जाँच करते हैं didOpen कार्यक्रम:

           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;
    }

हम इसे घटना में करते हैं क्योंकि सनल तुरंत खुला नहीं है popstate \ htmx:historyRestore (आम तौर पर अगर आपके पास बहुत सारे इतिहास हैं). तो हम इसके लिए में जाँच करने की जरूरत है didOpen घटना (यह सत्र कुंजी में किया जा रहा है, कभी कभी यह फिर से लोड किया जा सकता है... इसलिए हमें इसके बारे में अवगत होना चाहिए).

ऑन्टियम

तो यह है कि आप एक HMMAX लोड सूचक के रूप में स्वीज2 का उपयोग कर सकते हैं। यह एक हैक का एक सा है लेकिन यह काम करता है और यह एक ही पुस्तकालय का उपयोग करने के लिए एक अच्छा तरीका है, लोड क्षमा और संवाद की पुष्टि.9.

logo

©2024 Scott Galloway