# Barra de filtros: progreso, errores y pequeñas victorias

<!--category-- HTMX, ASP.NET, JavaScript -->
<datetime class="hidden">2025-11-10T14:10</datetime>

## Introducción

He estado preparando una barra de filtro adecuada para la lista de blogs: idioma, rango de fechas, ordenar y paginación que se mantiene ágil con HTMX.

Está cerca ahora, pero como siempre, el último 20% es el bit picante: fechas, cachés, y “¿por qué funciona localmente pero no después de un intercambio?”[Si desea la inmersión profunda con un montón de código y diagramas, vea:](/blog/filterbarprogressandissues)

> /blog/filterbarprogress and issues

[TOC]

## Recuerde que este sitio es un trabajo en progreso, este tipo de cosas va a suceder cuando usted come-su-propio-perro-alimento!

- TL;DR
- Nueva barra de filtros: Idioma + Ordenar + Rango de fechas
- Los swaps parciales HTMX mantienen la página rápida; funciona hacia atrás/hacia adelante porque siempre empujo la URL actualizada`/blog/calendar-days`Los momentos más destacados del calendario provienen de un pequeño
- endpoint
- Fijado: rangos de fechas que se bajan al cambiar de idioma/orden
- Fijo: calendario no refrescante después de swaps HTMX o cambios de tema

## El almacenamiento en caché del servidor ahora varía según todas las claves de consulta relevantes

- Lo que está funcionando bien
- Comportamiento URL-first: Todas las interacciones actualizan la URL; puede enlazar profundamente a un estado de filtro`LinkUrl`La paginación preserva los filtros actuales (
- se establece en el servidor)

```csharp
// Server: cache and vary for index
[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);
}
```

## El rango de Flatpicker por defecto a los valores de consulta actuales si está presente y resalta días con publicaciones

### Los errores que he corregido (y las nuevas características!)

Visibilidad y control de contenido

- **La mayor adición: gestión adecuada de la visibilidad post:**Puestos ocultos`IsHidden`: Los mensajes ahora pueden ser marcados como ocultos (
- **bandera) y no aparecerá en los listados**Publicación programada`ScheduledPublishDate`: Los mensajes pueden tener un
- **y sólo aparecen después de ese tiempo**Puestos fijados`IsPinned`: Puestos marcados con

```csharp
// BlogService now filters appropriately
var now = DateTimeOffset.UtcNow;
postQuery = postQuery.Where(x =>
    !x.IsHidden &&
    (x.ScheduledPublishDate == null || x.ScheduledPublishDate <= now));

// Pinned posts sorted first on page 1
if (isFirstPage)
{
    postQuery = postQuery.OrderByDescending(x => x.IsPinned)
                         .ThenByDescending(x => x.PublishedDate.DateTime);
}
```

### siempre aparece primero en la página 1 (genial para anuncios)

- **Mejoras en el rango de fechas`/blog/date-range`Nuevo**endpoint
- **: Devuelve las fechas de min/max en todos los mensajes (consciente de idioma) para que el selector de fechas pueda establecer límites razonables**Borrar param tag helper`<clear-param>`: Añadido

```cshtml
<clear-param name="startDate">Clear Date</clear-param>
<clear-param all="true" exclude="language">Clear All Filters</clear-param>
```

### tag helper para borrar fácilmente los parámetros de consulta

- Corrección de errores
  - Perder el intervalo de fechas al cambiar de idioma u orden`new URL(window.location.href)`Arreglar: Comenzar desde

```js
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', s.toISOString().substring(0,10));
    u.searchParams.set('endDate',   e.toISOString().substring(0,10));
  }
  applyNavigation(u);
});
```

- y solo anular los parámetros que cambian; si Flatpicker tiene una selección, vuelva a aplicarlo.
  
  - Calendario destaca no refrescante después de swaps HTMX`fp.redraw()`Arreglar: Destruir/recrear Flatpicker en init, a continuación, buscar puntos destacados para el mes visible y llamar`htmx:afterSwap`.

- ; también volver a ejecutar init en
  
  - Estilos de modo oscuro que no se aplican al calendario`<html class>`Fijar: observar`fp.redraw()`y llamar

## cuando cambia.

```mermaid
sequenceDiagram
  participant User
  participant JS as blog-index.js
  participant HT as HTMX
  participant S as Server

  User->>JS: Change language/order or pick a date range
  JS->>JS: Update URLSearchParams, pushState
  JS->>HT: htmx.ajax('GET', /blog?...)
  HT->>S: GET /blog
  S-->>HT: HTML partial (_BlogSummaryList)
  HT-->>User: Swap #content
```

## Cómo hablan las piezas

Detección de solicitud de página HTMX`pagerequest`La barra de filtros utiliza un filtro personalizado

```csharp
public static bool IsPageRequest(this HttpRequest request)
{
    return request.Headers.ContainsKey("pagerequest") &&
           request.Headers["pagerequest"] == "true";
}
```

encabezado para ayudar al servidor a distinguir entre cargas de página completas y swaps parciales de HTMX:

> **Esto permite que el controlador devuelva sólo la vista parcial para las solicitudes HTMX y vistas de página completa para la navegación directa.**Nota

## : La extensión de la etiqueta de paginación y el parámetro de consulta más claro componente Alpine.js merecen su propia inmersión profunda - voy a cubrir estos en detalle en un post futuro sobre la construcción de componentes reutilizables HTMX / Alpine.

- Aún no lo he hecho.
- Presets de fecha rápida (Últimos 7/30 días, Este año)
- Semilla local de almacenamiento para el idioma

Soporte de teclado en el selector de rango