Back to "फ़िल्टर पट्टी प्रगति (और तिथि सीमाएंरन्स)"

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

ASP.NET HTMX JavaScript

फ़िल्टर पट्टी प्रगति (और तिथि सीमाएंरन्स)

Monday, 10 November 2025

परिचय

पिछले कुछ दिनों के दौरान मैं नए ब्लॉग फ़िल्टर बार में बाहर जा रहा हूँ: भाषा चयन, आदेश, और एक तारीख चुनने की कोशिश कर रहे हैं कि स्मार्ट होने के लिए बहुत कठिन है।

यह पोस्ट जो मैंने बनाया था, वह मेरे लिए बहुत मायने रखता था, क्यों कुछ बातें ऐसी होती थीं (जो आगे नहीं बताती, तरोताज़ा नहीं करती थीं) और मैं ने उन्हें कैसे रखा ।

स्रोत कोड की भरपूरता और एक और दो मेरौंग चित्र दिखाने के लिए।

याद रखें कि इस साइट की प्रगति में एक काम है, सामान के इस प्रकार होगा जब आप अपने-अपने-छोटे भोजन खाना खाते हैं!

  • किस बात ने बदल दी?
  • यहाँ उच्च स्तर है: मैंने ब्लॉग इंडेक्स में एक उचित फ़िल्टर पट्टी जोड़ा जिसमें शामिल है:
  • भाषा चुना जाता है कि आपके वर्तमान फ़िल्टर की रक्षा करता है तथा HMMMX के जरिए सूची फिर से सेट करता है
  • छांटें अनुक्रम चयन (तिथि/ टिप्पणी) जो कि संदर्भ की रक्षा करता है
  • सर्वर हाल के दिनों में तारीख सीमा चयनक (Frolter)

URL स्थिति हमेशा सिंक में रखा जाता है (बैक/ आगे काम करता है, गहरे लिंक का कार्य)

  • सर्वर कैशिंग अब फ़िल्टर पैरामीटर्स के द्वारा सही प्रकार से बदल रहा हैइस अद्यतन में नया (बड़ा ठीक!)
  • दृश्यता फिल्टर: अब पोस्ट को छुपाया जा सकता है, भविष्य के प्रकाशन के लिए नियत किया जा सकता है, या ऊपर तक ले जाया जा सकता है/blog/date-rangeतिथि सीमा अंत बिन्दु
  • : नयाएपीआई का परिणाम होगा न्यूनतम/max तारीख़ का परिणाम होगा समझदार चाह्टर सीमा के लिए<clear-param>साफ करें

: फिल्टरों को साफ करने का आसान तरीका

अनपेक्षित शक्‍ति के साथ टैग सहायता

जिस तरह से मैंने कुछ बग बनाया था: तारीख सीमा को भाषा/क्रमित करने के लिए खो दिया जा रहा है, पंचांग समायोजन पर ताज़ा नहीं है, और एक मजेदार व्यक्ति जहां अंधेरा मोड स्टाइल टूटी क्योंकि storting फिर नहीं मिला था.

<div id="filters" hx-target="#content">
  <select id="languageSelect">…</select>
  <select id="orderSelect">
    <option value="date_desc">Newest first</option>
    <option value="date_asc">Oldest first</option>
    <option value="title_asc">Title A–Z</option>
    <option value="title_desc">Title Z–A</option>
  </select>
  <input id="dateRange" type="text" placeholder="YYYY-MM-DD → YYYY-MM-DD" />
  <button id="clearDateFilter">Clear</button>
  <div id="filterSummary"></div>
</div>

<div id="content"><!-- HTMX swaps blog list here --></div>

डॉम में फ़िल्टर पट्टी

स्क्रिप्ट उम्मीद करता है कि यह समान संरचना है:Mostlylucid/src/js/blog-index.jsक्लाएंट: HMAX + समतल क़ौमज़िंग

  • व्यवहार में जीवन कोर
function applyNavigation(u){
  const target = document.querySelector('#content');
  try{ window.history.pushState({}, '', u.toString()); }catch{}
  if(window.htmx && target){
    window.htmx.ajax('GET', u.toString(), {
      target: '#content',
      swap: 'outerHTML show:none',
      headers: {'pagerequest': 'true'}
    });
  } else {
    window.location.href = u.toString();
  }
}
const url = new URL(window.location.href);
const existingStart = url.searchParams.get('startDate');
const existingEnd   = url.searchParams.get('endDate');
const existingLang  = url.searchParams.get('language') || 'en';
const existingOrderBy = (url.searchParams.get('orderBy') || 'date').toLowerCase();
const existingOrderDir = (url.searchParams.get('orderDir') || 'desc').toLowerCase();

langSelect.value = existingLang;
orderSelect.value = `${existingOrderBy}_${existingOrderDir}`;
updateSummary();
  • कुछ महत्वपूर्ण टुकड़े:
async function fetchMonth(year, month, language){
  const res = await fetch(`/blog/calendar-days?year=${year}&month=${month}&language=${encodeURIComponent(language||'en')}`);
  if(!res.ok) return new Set();
  const j = await res.json();
  return new Set(j.dates || []);
}

function formatYMD(d){ return d.toISOString().substring(0,10); }

let highlightDates = new Set();
const fp = window.flatpickr(input, {
  mode: 'range',
  dateFormat: 'Y-m-d',
  defaultDate: [existingStart, existingEnd].filter(Boolean),
  onDayCreate: function(_dObj,_dStr,fpInstance,dayElem){
    const ymd = formatYMD(dayElem.dateObj);
    if(highlightDates.has(ymd)){
      dayElem.classList.add('has-post');
      dayElem.style.background = 'rgba(76,175,80,0.35)';
      dayElem.style.borderRadius = '6px';
    }
  },
  onMonthChange: async function(_sd,_ds,fpInstance){
    highlightDates = await fetchMonth(fpInstance.currentYear, fpInstance.currentMonth+1, langSelect.value);
    fpInstance.redraw();
  },
  onOpen: async function(_sd,_ds,fpInstance){
    highlightDates = await fetchMonth(fpInstance.currentYear, fpInstance.currentMonth+1, langSelect.value);
    fpInstance.redraw();
  },
  onChange: function(selectedDates){
    if(selectedDates.length === 2){
      const [start,end] = selectedDates;
      const u = new URL(window.location.href);
      u.searchParams.set('startDate', formatYMD(start));
      u.searchParams.set('endDate', formatYMD(end));
      u.searchParams.set('page','1');
      u.searchParams.set('language', langSelect.value);
      const [ob,od] = orderSelect.value.split('_');
      u.searchParams.set('orderBy', ob);
      u.searchParams.set('orderDir', od);
      updateSummary();
      applyNavigation(u);
    }
  }
});
  • यूआरएल सुरक्षा तथा HMMX नेविगेशन
langSelect.addEventListener('change', async function(){
  const u = new URL(window.location.href);
  u.searchParams.set('language', langSelect.value);
  u.searchParams.set('page', '1');
  const [ob,od] = (orderSelect.value||'date_desc').split('_');
  u.searchParams.set('orderBy', ob);
  u.searchParams.set('orderDir', od);
  if(input._flatpickr && input._flatpickr.selectedDates.length===2){
    const [s,e] = input._flatpickr.selectedDates;
    u.searchParams.set('startDate', formatYMD(s));
    u.searchParams.set('endDate', formatYMD(e));
  }
  // refresh calendar highlights for new language
  if(input._flatpickr){
    const fp = input._flatpickr;
    highlightDates = await fetchMonth(fp.currentYear, fp.currentMonth+1, langSelect.value);
    fp.redraw();
  }
  updateSummary();
  applyNavigation(u);
});
  • आरंभ करें, यूआरएल से डिफ़ॉल्टing, और सारांश यूआई को सिंक में रखें
const obs = new MutationObserver(() => {
  const dr = root.querySelector('#dateRange');
  const fp = dr && dr._flatpickr;
  if(fp && typeof fp.redraw === 'function') fp.redraw();
});
obs.observe(document.documentElement, { attributes:true, attributeFilter:['class'] });

सर्वर वुल्फ के दिन के साथ समतल भूनार सीमा

जब भाषा या क्रम बदलता है तो तिथि बचाए रखें

  • फिर से नया कैलेण्डर बनाएँ जब प्रसंग टॉगल करता है (गुप्त/ प्रकाश) तो समतल करनेवाला फिर सेपेक्ट करता है*
  • सर्वर: अंत- बिन्दु तथा क कैशिंगcalendar-daysपृष्ठ पर तीन अंत- बिन्दुओं की शक्‍ति:
  • निर्देशिका जो पृष्ठ स्वीकारता है, पृष्ठ आकार स्वीकारता है, कॉम- सिस्टम, भाषा तथा अनुक्रम प्रारंभ करेंdate-rangeए
// GET /blog
[HttpGet]
[ResponseCache(Duration = 300, VaryByHeader = "hx-request",
  VaryByQueryKeys = new[] { "page", "pageSize", nameof(startDate), nameof(endDate), nameof(language), nameof(orderBy), nameof(orderDir) },
  Location = ResponseCacheLocation.Any)]
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" },
  VaryByQueryKeys = new[] { nameof(page), nameof(pageSize), nameof(startDate), nameof(endDate), nameof(language), nameof(orderBy), nameof(orderDir) })]
public async Task<IActionResult> Index(int page = 1, int pageSize = 20, DateTime? startDate = null, DateTime? endDate = null,
  string language = MarkdownBaseService.EnglishLanguage, string orderBy = "date", string orderDir = "desc")
{
    var posts = await blogViewService.GetPagedPosts(page, pageSize, language: language, startDate: startDate, endDate: endDate);
    posts.LinkUrl = Url.Action("Index", "Blog", new { startDate, endDate, language, orderBy, orderDir });
    if (Request.IsHtmx()) return PartialView("_BlogSummaryList", posts);
    return View("Index", posts);
}

// GET /blog/calendar-days?year=2025&month=11&language=en
[HttpGet("calendar-days")]
[ResponseCache(Duration = 300, VaryByHeader = "hx-request", VaryByQueryKeys = new[] { nameof(year), nameof(month), nameof(language) }, Location = ResponseCacheLocation.Any)]
[OutputCache(Duration = 1800, VaryByHeaderNames = new[] { "hx-request" }, VaryByQueryKeys = new[] { nameof(year), nameof(month), nameof(language) })]
public async Task<IActionResult> CalendarDays(int year, int month, string language = MarkdownBaseService.EnglishLanguage)
{
    if (year < 2000 || month < 1 || month > 12) return BadRequest("Invalid year or month");
    var start = new DateTime(year, month, 1);
    var end = start.AddMonths(1).AddDays(-1);
    var posts = await blogViewService.GetPostsForRange(start, end, language: language);
    if (posts is null) return Json("");
    var dates = posts.Select(p => p.PublishedDate.Date).Distinct().OrderBy(d => d).Select(d => d.ToString("yyyy-MM-dd")).ToList();
    return Json(new { dates });
}

// GET /blog/date-range?language=en
[HttpGet("date-range")]
[ResponseCache(Duration = 3600, VaryByHeader = "hx-request", VaryByQueryKeys = new[] { nameof(language) }, Location = ResponseCacheLocation.Any)]
[OutputCache(Duration = 7200, VaryByHeaderNames = new[] { "hx-request" }, VaryByQueryKeys = new[] { nameof(language) })]
public async Task<IActionResult> DateRange(string language = MarkdownBaseService.EnglishLanguage)
{
    var allPosts = await blogViewService.GetAllPosts();
    if (allPosts is null || !allPosts.Any())
    {
        return Json(new
        {
            minDate = DateTime.UtcNow.AddYears(-1).ToString("yyyy-MM-dd"),
            maxDate = DateTime.UtcNow.ToString("yyyy-MM-dd")
        });
    }

    var posts = allPosts;
    if (!string.IsNullOrEmpty(language) && language != MarkdownBaseService.EnglishLanguage)
    {
        posts = allPosts.Where(p => p.Language.Equals(language, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    if (!posts.Any()) posts = allPosts;

    var minDate = posts.Min(p => p.PublishedDate.Date);
    var maxDate = posts.Max(p => p.PublishedDate.Date);

    return Json(new
    {
        minDate = minDate.ToString("yyyy-MM-dd"),
        maxDate = maxDate.ToString("yyyy-MM-dd")
    });
}

अंतिम बिन्दु जो दिए गए माह में दिन के सेट को लौटाता है, जिसके पोस्ट होते हैं ( विशेषताओं के लिए)LinkUrlए

public class BasePagingModel<T> : Interfaces.IPagingModel<T> where T : class
{
    public int Page { get; set; }
    public int TotalItems { get; set; } = 0;
    public int PageSize { get; set; }
    public ViewType ViewType { get; set; } = ViewType.TailwindAndDaisy;
    public string LinkUrl { get; set; }
    public List<T> Data { get; set; }
}

अंत बिन्दु जो कि न्यूनतम/max तारीख़ बताता है सभी पोस्टों के पार (अंग्रेषित)

sequenceDiagram
  participant U as User
  participant FP as Flatpickr
  participant JS as blog-index.js
  participant HT as HTMX
  participant C as BlogController

  U->>FP: Selects 2025-11-01 → 2025-11-10
  FP-->>JS: onChange([start,end])
  JS->>JS: Update URLSearchParams (startDate, endDate, language, order)
  JS->>HT: htmx.ajax('GET', /blog?...)
  HT->>C: GET /blog with query
  C-->>HT: Partial _BlogSummaryList
  HT-->>U: Swap #content
  JS->>FP: (after swap) ensure highlights + redraw

एक तत्काल तरफ: pances मॉडल है

flowchart TD
  A[Open calendar / Month change] --> B[Fetch /blog/calendar-days]
  B -->|JSON dates: yyyy-mm-dd array| C[Set highlightDates]
  C --> D[flatpickr redraw]
  D --> E[onDayCreate adds .has-post]

कि जब सर्वर का अनुवाद किया जाता है तो मौजूदा फ़िल्टर संदर्भों की सुरक्षा करता है.

अनुक्रम:

प्रवाह: कैलेण्डर विशेष करता हैBlogPostEntity:

public class BlogPostEntity
{
    // ... existing fields ...

    public bool IsPinned { get; set; }
    public bool IsHidden { get; set; }
    public DateTimeOffset? ScheduledPublishDate { get; set; }
}

नयी विशेषताएँ: दृश्यता और सामग्री नियंत्रणBlogServiceपोस्ट दृश्यता फ़िल्टरName

// Filter out hidden posts and posts scheduled for the future
var now = DateTimeOffset.UtcNow;
postQuery = postQuery.Where(x =>
    !x.IsHidden &&
    (x.ScheduledPublishDate == null || x.ScheduledPublishDate <= now));

// For page 1, prioritize pinned posts
var isFirstPage = page == null || page.Value == 1;
if (isFirstPage)
{
    postQuery = postQuery.OrderByDescending(x => x.IsPinned)
                         .ThenByDescending(x => x.PublishedDate.DateTime);
}

इस अद्यतन में सबसे बड़ा सुधार उचित प्रेस प्रबंधन है.

  • तीन नए खेत जोड़े गए (IsHidden = trueवह
  • **अब फ़िल्टर पोस्ट उचित:**इसका अर्थ है:ScheduledPublishDate
  • छुपी पोस्ट्स (IsPinned = true) लिस्ट्स में कभी नहीं प्रकट होता

समयबद्ध पोस्ट- पोस्ट

सिर्फ उनके बाद ही प्रकट हो जाते हैं

[HtmlTargetElement("clear-param")]
public class ClearParamTagHelper : TagHelper
{
    [HtmlAttributeName("name")]
    public string? Name { get; set; }

    [HtmlAttributeName("all")]
    public bool All { get; set; } = false;

    [HtmlAttributeName("exclude")]
    public string Exclude { get; set; } = "";

    // ... styling and Alpine.js integration ...
}

पोस्ट- पोस्ट

<!-- Clear a specific parameter -->
<clear-param name="startDate">Clear Date</clear-param>

<!-- Clear all parameters except language -->
<clear-param all="true" exclude="language">Clear All Filters</clear-param>

) हमेशा प्रथम पृष्ठ १ पर आता है घोषणाओं या विशिष्ट सामग्री के लिएwindow.queryParamClearerपारम टैग सहायक

**एक नया. neNT टैग मदद को स्पष्ट क्वैरी पैरामीटर्स के लिए आसान बनाता है:**वर्व दृश्य में उपयोग:

टैग सहायता Ajs.js के साथ एकीकृत

  • यूआरएल का उपयोग कर तथा HMMMX ट्रैक्स को हैंडल करने के लिए घटक.

    • टिप्पणीstartDate/endDate.
    • : दोनों सक्रिय टैग सहायक तथा क्वैरी पैरामीटर अधिक गहरे खोज के लायक हैं.new URL(window.location.href)मैं पूरी कार्यान्वयन विवरण को कवर करूंगा, जिसमें Ajson घटक और टैग सहायक पैटर्न शामिल हैं, निर्माण के बारे में एक भावी पोस्ट मेंlanguage/orderबग मैं हिट (और फिक्सेस)
  • भाषा/क्रम परिवर्तन पर तिथि सीमा गुम हो गई है

    • कारण: मैं ने मौजूदा में से नहीं बल्कि मौजूदा में से नया यूआरएल बनाया है, खो गया है
    • ठीक करें: हमेशा प्रारंभ करें
  • "और व्यर्थ बात और कठ-तुला में पड़े रहनेवालों के सिवा कुछ भी नहीं होता

    • देखें
    • ऊपर हैंडलर बदल देते हैं.<html class>HMMX साम्हने के बाद पंचांग को विशिष्ट किया गयाfp.redraw().
  • कारण: प्लैटर उदाहरण पुराने डोम पर रहता था और रोंगटे खड़े हो जाता था ।

    • ठीक है: किसी भी मौजूदा उदाहरण को नष्ट करने के लिए फिर से बनाएँ, फिर दृश्‍य महीने के लिए विशिष्ट विशेषताएँ लोड करें ।LinkUrlस्वेप के बाद फिर से दोबारा नज़रअंदाज़ करें (नीचे देखें)
    • गहरे मोड ने कैलेण्डर को टूटा रूप में देखाNameposts.LinkUrl = Url.Action("Index","Blog", new { startDate, endDate, language, orderBy, orderDir })कारण: शैली की क्लास बदल गयी, लेकिन समतल भू - दृश्‍य रंग के दिन फिर से नहीं निकाले गए ।

ठीक है: ध्यान दें

परिवर्तन और कॉल

(function(){
  function initFromRoot(root){ /* …the big function shown above… */ }
  if(window.htmx){
    document.body.addEventListener('htmx:afterSwap', function(ev){
      const tgt = ev.detail.target;
      // re-init if the content swapped includes the filters/content area
      if(tgt && (tgt.id === 'content' || tgt.querySelector?.('#filters'))){
        initFromRoot(document);
      }
    });
  }
  // also run on first load
  initFromRoot(document);
})();

प्रवेश किया जा रहा लिंक फ़िल्टर संदर्भ छोड़ा गया

  • कारण:
  • कुछ मामलों में क्वैरी को शामिल करने के लिए सेट नहीं किया गया था ।
  • ठीक करें: सर्वर पर, सेट

इस प्रकार पृष्ठ लिखना यूआरएल को सही तरह से वितरित करता है.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.