2025, Oct 22 13:46
HTMX WebSocket में DOM खाली? Django Channels उदाहरण से स्वैप सही करने का तरीका
HTMX WebSocket में संदेश दिखे पर DOM खाली रहे? Django Channels से कारण जानें और सही स्वैप हेतु .stats-wrapper या hx-swap-oob के साथ समाधान अपनाएं आज ही.
WebSocket पर HTMX कभी-कभी “काम करता है पर कुछ रेंडर नहीं होता”: DevTools में सर्वर का संदेश दिखता है, फिर भी लक्ष्य क्षेत्र खाली रहता है। यह गाइड Django Channels के एक ठोस उदाहरण से समझाती है कि DOM खाली क्यों रहता है और स्वैप को वास्तव में कैसे ट्रिगर किया जाए।
समस्या का अवलोकन
कनेक्ट होते ही एक WebSocket consumer रेंडर किया हुआ HTML फ्रैगमेंट भेजता है। पेज HTMX WebSocket एक्सटेंशन से वही फ्रैगमेंट प्राप्त करता है और उसे टारगेट कंटेनर में स्वैप करने की कोशिश करता है। संदेश पहुँच जाता है, लेकिन डैशबोर्ड खाली रहता है।
पुनरुत्पादन हेतु कोड
सर्वर-साइड consumer जो काउंट्स रेंडर करता है और क्लाइंट को पुश करता है:
from channels.generic.websocket import WebsocketConsumer
from django.template.loader import render_to_string
from myapp.models import Model1, Model2, Model3, Model4
class MetricsBannerSocket(WebsocketConsumer):
    def push_totals(self):
        figures = [
            {"label": "Model1", "total": Model1.objects.count()},
            {"label": "Model2", "total": Model2.objects.count()},
            {"label": "Model3", "total": Model3.objects.count()},
            {"label": "Model4", "total": Model4.objects.count()},
        ]
        html_payload = render_to_string("dashboard/metrics-fragment.html", {"figures": figures})
        self.send(text_data=html_payload)
    def connect(self):
        self.accept()
        self.push_totals()
स्टैट्स रेंडर करने के लिए उपयोग हुआ HTML फ़्रैगमेंट:
{% for row in figures %}
<div class="col-sm-6 col-xl-3">
    <div class="dashboard-stat rounded d-flex align-items-center justify-content-between p-4">
        <div class="ms-3">
            <p class="mb-2">{{ row.label }}</p>
            <h6 class="mb-0">{{ row.total }}</h6>
        </div>
    </div>
</div>
{% endfor %}
क्लाइंट-साइड टेम्पलेट, जो WebSocket कनेक्शन बनाता है और स्वैप का व्यवहार घोषित करता है:
{% load static %}
{% block styles %}
    <link rel="stylesheet" href="{% static 'css/dashboard.css' %}">
{% endblock %}
<div class="container-fluid pt-4 px-4">
    <div class="row g-2 mb-2 stats-wrapper"
         hx-ext="ws"
         ws-connect="/ws/dashboard/header/"
         hx-target=".stats-wrapper"
         hx-swap="innerHTML"
    >
    </div>
    <div class="active-tasks-scroll-container">
        <div class="row flex-nowrap g-2">
        </div>
    </div>
</div>
मूल कारण
सर्वर ऐसा सादा HTML भेज रहा है जिसमें .stats-wrapper एलिमेंट शामिल नहीं है। स्वैप टारगेट .stats-wrapper ढूंढ रहा है, पर प्रतिक्रिया वाले फ्रैगमेंट में वह मौजूद नहीं, इसलिए HTMX के पास मैच करने का कोई एलिमेंट नहीं रहता और DOM में कुछ भी रिप्लेस नहीं होता। आप नेटवर्क पैनल में संदेश देख लेते हैं, फिर भी रेंडरिंग नहीं होती क्योंकि रिस्पॉन्स में वह टारगेट कंटेनर नहीं आता जिसकी अपेक्षा HTMX को होती है।
इसे कैसे ठीक करें
WebSocket पेलोड के साथ स्वैप को भरोसेमंद बनाने के दो सरल तरीके हैं।
पहला विकल्प: रिस्पॉन्स पेलोड में रैपर भी शामिल करें ताकि HTMX .stats-wrapper को ढूंढ सके और रिप्लेसमेंट कर दे। यह संशोधित टेम्पलेट देखें:
<div class="row g-2 mb-2 stats-wrapper">
    {% for row in figures %}
    <div class="col-sm-6 col-xl-3">
        <div class="dashboard-stat rounded d-flex align-items-center justify-content-between p-4">
            <div class="ms-3">
                <p class="mb-2">{{ row.label }}</p>
                <h6 class="mb-0">{{ row.total }}</h6>
            </div>
        </div>
    </div>
    {% endfor %}
</div>
दूसरा विकल्प: केवल अंदर की सामग्री भेजते रहें और रिस्पॉन्स को out-of-band बनाएं, ताकि HTMX DOM में मौजूद .stats-wrapper को ढूंढकर रिप्लेस कर दे — भले ही वह वर्तमान एलिमेंट का सीधा स्वैप टारगेट न हो।
<div class="row g-2 mb-2 stats-wrapper" hx-swap-oob="true">
    {% for row in figures %}
    <div class="col-sm-6 col-xl-3">
        <div class="dashboard-stat rounded d-flex align-items-center justify-content-between p-4">
            <div class="ms-3">
                <p class="mb-2">{{ row.label }}</p>
                <h6 class="mb-0">{{ row.total }}</h6>
            </div>
        </div>
    </div>
    {% endfor %}
</div>
इनमें से किसी भी तरीके से HTMX मेल खाता .stats-wrapper एलिमेंट ढूंढ पाता है और स्वैप कर देता है, इसलिए काउंट्स अपेक्षित रूप से दिखाई देते हैं।
यह क्यों मायने रखता है
जब आप सर्वर‑रेंडर किए फ्रैगमेंट ब्राउज़र में स्ट्रीम करते हैं, तो HTMX जिस स्वैप टारगेट का उपयोग करता है, वह या तो पेलोड में मौजूद होना चाहिए या out-of-band प्रतिस्थापन के लिए चिह्नित होना चाहिए। यदि आने वाले HTML में वह एलिमेंट नहीं है जिसे HTMX बदलना चाहता है, तो उपयोगकर्ता के नजरिए से अपडेट चुपचाप असफल हो जाता है। यह सुनिश्चित करना कि टारगेट या तो रिस्पॉन्स में है या OOB सेमैटिक्स के माध्यम से घोषित है, WebSocket-चालित UI को पूर्वानुमेय और डिबग करने लायक रखता है।
निष्कर्ष
यदि WebSocket रिस्पॉन्स सफलतापूर्वक आने के बावजूद कुछ रेंडर नहीं करता, तो जाँचें कि फ्रैगमेंट में वह एलिमेंट शामिल है जिसे HTMX को स्वैप करना है, या फिर प्रतिस्थापन को निर्देशित करने के लिए hx-swap-oob का उपयोग करें। यह छोटा-सा संरचनात्मक विवरण ही अक्सर खाली डैशबोर्ड और लाइव, अपडेट होता हेडर—इनके बीच अंतर बना देता है।
यह लेख StackOverflow पर प्रश्न (लेखक: user31153869) और Mahrez BenHamad के उत्तर पर आधारित है।