# Un Componente de Visualización de Pago ASP.NET Core Tag Helper (Part 2, PageSizeM SK3

<datetime class="hidden">2025-03-25T22:12</datetime>

<!--category-- ASP.NET Core, TagHelper, PagingTagHelper -->
# El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Como parte de mi sage continuo con mi ayudante de tag paging ahora he separado PageSize en su propio ayudante para tag. Esto es para hacer el ayudante del tag más flexible y permitir para escenarios de paging más complejos.

**De nuevo; estos son todos trabajos -en curso. IM SK3m ya [El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre los hombres y las mujeres en materia de empleo, incluida la eliminación del trabajo forzoso.](https://www.nuget.org/packages/mostlylucid.pagingtaghelper#readme-body-tab) a la hora de escribir pero USES A SU OWN RISK. Es' es gratuito, pero NO YET de calidad de lanzamiento IMHO M SK2No tengo [El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.](https://taghelpersample.mostlylucid.net/PageSizeWithHTMX) El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre los hombres y las mujeres en materia de empleo, incluida la eliminación del trabajo forzoso.**

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. [código para el ayudante de etiquetas aquí](https://github.com/scottgal/mostlylucid.pagingtaghelper/blob/main/mostlylucid.pagingtaghelper/Components/PageSizeTagHelper.cs).

[TOC]

# El asistente de etiquetas de tamaño de página

Como con mis otros ayudantes de etiquetas esto es diseñado para un uso-causa que a menudo encuentro; cómo cambiar fácilmente el tamaño de la página en las listas de resultadosM SK2
A primera vista esto parece bastante sencillo, pero puede convertirse en TRICKY una vez que se añada HTMX al mix.

## El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Así que para mi caso de uso los requisitos eran los siguientes:

1. Permite un uso fácil para el desarrollador te (la medida en que sea posible se configura usando un modelo de página simple)
2. Soporta actualizar el tamaño de la página usando HTMX - lo que significa que es capaz de enviarse de nuevo *El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.*.
   Esto es importante porque permite al usuario cambiar el tamaño de la página sin perder ningún otro filtro que haya aplicado.
3. Tiene un rango relativamente dinámico de tamaños de páginas (incluyendo una opción 'AllM SK2

## La ayudante del etiquetado

Con esto en mente, he diseñado el TagHelper para ser el siguiente:

```html
<page-size
    hx-target="#list"
    hx-swap="innerHTML"
    model="Model">
    
</page-size>
<div id="list">
    <partial name="_ResultsList" model="Model"/>
</div>
```

Así que en este caso de nuevo es necesario mi modelo de paging estándar:

```csharp
public interface IPagingModel
{
    public int Page { get; set; }
    public int TotalItems { get; set; }
    public int PageSize { get; set; }

    public ViewType ViewType { get; set; }
    
    public string LinkUrl { get; set; }
}
```

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `Page`, `TotalItems`, `PageSize`, `ViewType` El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `LinkUrl` Para el correo electrónico.

También se pueden especificar en forma de atributos para el modelo:

```html

<page-size
    hx-target="#list"
    hx-swap="innerHTML"
    total-items="100"
    page-size="10"
    view-type="DaisyAndTailwind">
</page-size>
```

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `hx-target` aquí es el objetivo para la solicitud HTMX y el `hx-swap` El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre hombres y mujeres en materia de empleo, incluida la eliminación del trabajo forzoso.

En este caso ' utilizará el especificado `PageSize` El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `TotalItems` El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `ViewType` para render el selector de tamaño de página.

## El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre hombres y mujeres en materia de empleo, incluida la adopción de medidas para eliminar la discriminación contra la mujer.

Por desgracia esta vez alrededor tenía una exigencia que no podía manejar'no solo el lado del servidor. Es decir, quería conservar los parámetros de cadena de consulta en la petición de cambio de tamaño de páginaM SK2
Para conseguir esto tengo dos piezas de JS que se cargarán en el control dependiendo del uso de HTMX oor no

```csharp
@if (Model.UseHtmx)
{
@Html.HTMXPageSizeChange()
}
else
{
    @Html.PageSizeOnchangeSnippet()
}
```

Si se está utilizando HTMX lo convierte en la página:

```javascript
(() => {
    if (window.__pageSizeListenerAdded) return;

    document.addEventListener('htmx:configRequest', event => {
        const { elt } = event.detail;
        if (elt?.matches('[name="pageSize"]')) {
            const params = new URLSearchParams(window.location.search);
            params.set('pageSize', elt.value); // This will update or add the pageSize param
            event.detail.parameters = Object.fromEntries(params.entries());
        }
    });
    window.__pageSizeListenerAdded = true;
})();
```

Que es una pieza simple de JavaScript que introducirá los parámetros actuales de la cadena de consulta en la solicitud.

Por otra parte, si HTMX no está activado, reproducirá esto:

```javascript
(function attachPageSizeListener() {
    // Ensure we only attach once if this script is included multiple times
    if (window.__pageSizeListenerAttached) return;
    window.__pageSizeListenerAttached = true;

    // Wait for the DOM to be fully loaded
    document.addEventListener("DOMContentLoaded", () => {
        document.body.addEventListener("change", handlePageSizeChange);
    });

    function handlePageSizeChange(event) {
        // Check if the changed element is a page-size <select> inside .page-size-container
        const select = event.target.closest(".page-size-container select[name='pageSize']");
        if (!select) return;

        const container = select.closest(".page-size-container");
        if (!container) return;

        // Default to "true" if there's no .useHtmx input
        const useHtmx = container.querySelector("input.useHtmx")?.value === "true";
        if (useHtmx) {
            // If using HTMX, we do nothing—HTMX will handle the request
            return;
        }

        // Either use a linkUrl from the container or the current page URL
        const linkUrl = container.querySelector("input.linkUrl")?.value || window.location.href;
        const url = new URL(linkUrl, window.location.origin);

        // Copy existing query params from current location
        const existingParams = new URLSearchParams(window.location.search);
        for (const [key, value] of existingParams.entries()) {
            url.searchParams.set(key, value);
        }

        // If user picked the same pageSize as what's already in the URL, do nothing
        if (url.searchParams.get("pageSize") === select.value) {
            return;
        }

        // Update the pageSize param
        url.searchParams.set("pageSize", select.value);

        // Redirect
        window.location.href = url.toString();
    }
})();
```

En la misma sesión, el Consejo aprobó el proyecto de resolución A/C.1/60/L.20 sin someterlo a votación (véase párr. `__pageSizeListenerAttached ` ya que es un poco demasiado genérico Y lo uso tanto para HTMX como no-HTMX peticiones.

## La Comisión Consultiva en Asuntos Administrativos y de Presupuesto recomienda a la Asamblea General que apruebe el siguiente proyecto de resolución:

El propio ayudante de etiquetas es bastante simple, el código principal sólo popula algunas propiedades y luego transmite la vista Componente.

```csharp
 public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        // We want to render a <div> by default
        output.TagName = "div";

        // Remove any leftover attributes that we handle ourselves
        RemoveUnwantedAttributes(output);

        // Determine final PagerId
        var pagerId = PagerId ?? PageSizeModel?.PagerId ?? $"pager-{Guid.NewGuid():N}";
        // Assign ID to the outer div
        output.Attributes.SetAttribute("id", pagerId);
        // Build or fallback to a link URL
        var finalLinkUrl = BuildLinkUrl();
        if (string.IsNullOrEmpty(finalLinkUrl))
        {
            // If we can't build a URL, show a fallback message or short-circuit
            output.Content.SetHtmlContent(
                "<p style=\"color:red\">No valid link URL found for PageSize control.</p>");
            return;
        }

        var finalPageSize = PageSize ?? PageSizeModel?.PageSize ?? 10;
        // Clamp the page size to MaxPageSize

        var finalTotalItems = TotalItems ?? Model?.TotalItems ?? PageSizeModel?.TotalItems ?? 0;
        if (finalTotalItems == 0) throw new ArgumentNullException(nameof(finalTotalItems), "TotalItems is required");
        var maxPageSize = Math.Min(finalTotalItems, MaxPageSize);

        // Fallback to model's properties if not set
        var finalViewType = PageSizeModel?.ViewType ?? Model?.ViewType ?? ViewType;

        var pageSizeSteps = new int[] { 10, 25, 50, 75, 100, 125, 150, 200, 250, 500, 1000 };

        
        if (!string.IsNullOrEmpty(PageSizeSteps))
            pageSizeSteps = PageSizeSteps.Split(',').Select(s =>

            {
                if (int.TryParse(s, out var result))
                    return result;
                else
                {
                    throw new ArgumentException("Invalid page size step", nameof(PageSizeSteps));
                }
            }).ToArray();


        var pageSizes = CalculatePageSizes(finalTotalItems, maxPageSize, pageSizeSteps);

        var useHtmx = PageSizeModel?.UseHtmx ?? UseHtmx;
        var useLocalView = PageSizeModel?.UseLocalView ?? UseLocalView;

        // Acquire the IViewComponentHelper
        var viewComponentHelper = ViewContext.HttpContext.RequestServices
            .GetRequiredService<IViewComponentHelper>();
        ((IViewContextAware)viewComponentHelper).Contextualize(ViewContext);

        // Construct the PageSizeModel (if not provided) with final settings
        var pagerModel = new PageSizeModel
        {
            ViewType = finalViewType,
            UseLocalView = useLocalView,
            UseHtmx = useHtmx,
            PageSizes = pageSizes,
            PagerId = pagerId,
            Model = Model,
            LinkUrl = finalLinkUrl,
            MaxPageSize = maxPageSize,
            PageSize = finalPageSize,
            TotalItems = finalTotalItems
        };

        // Safely invoke the "PageSize" view component
        try
        {
            var result = await viewComponentHelper.InvokeAsync("PageSize", pagerModel);
            output.Content.SetHtmlContent(result);
        }
        catch (Exception ex)
        {
            // Optional: Log or display an error
            output.Content.SetHtmlContent(
                $"<p class=\"text-red-500\">Failed to render PageSize component: {ex.Message}</p>"
            );
        }
    }
```

Una pieza difícil es manejar correctamente el `PageSizeSteps` attribute. Esta es una lista separada por comillas de tamaños de página que el usuario puede seleccionar desde. O usted puede pasar en sus propios pasosM SK2 También se puede especificar el MaxPageSize que es el tamaño máximo de la página que puede ser seleccionado .

```csharp
    private List<int> CalculatePageSizes(int totalItems, int maxxPageSize, int[] pageSizeSteps)
    {
        var pageSizes = new List<int>();

        // 1. Include all fixed steps up to both TotalItems and MaxPageSize
        foreach (var step in pageSizeSteps)
        {
            if (step <= totalItems && step <= maxxPageSize)
                pageSizes.Add(step);
        }

        // 2. If TotalItems exceeds the largest fixed step, keep doubling
        int lastFixedStep = pageSizeSteps.Last();
        if (totalItems > lastFixedStep)
        {
            int next = lastFixedStep;
            while (next < totalItems && next < maxxPageSize)
            {
                next *= 2; // double the step
                if (next <= totalItems && next <= maxxPageSize)
                {
                    pageSizes.Add(next);
                }
            }
        }

        // 3. Include TotalItems if it's not already in the list
        if (totalItems <= maxxPageSize && !pageSizes.Contains(totalItems))
        {
            pageSizes.Add(totalItems);
        }

        pageSizes.Sort();

        return pageSizes;
    }

```

Como con mi otro ayudante de etiquetas yo uso el padrón TagHelper->ViewComponent para render la componenta de vista. Esto me permite mantener el ayudante del etiqueta simple y la compleja de la componente de visiónM SK2 También le permite cambiar las vistas para diferentes experiencias ; o incluso inyectar su propia vista y usar que MSC4using `use-local-view`).

```csharp

public class PageSizeViewComponent : ViewComponent
    {
        public IViewComponentResult Invoke(PageSizeModel model)
        {
            if(model.Model != null)
            {
                model.LinkUrl ??= model.Model.LinkUrl;
                
                if(model.Model is IPagingSearchModel searchModel)
                {
                    model.SearchTerm ??= searchModel.SearchTerm;
                }
            }
            
            var viewName = "Components/Pager/Default";

            var useLocalView = model.UseLocalView;

            return (useLocalView, model.ViewType) switch
            {
                (true, ViewType.Custom) when ViewExists(viewName) => View(viewName, model),
                (true, ViewType.Custom) when !ViewExists(viewName) => throw new ArgumentException("View not found: " + viewName),
                (false, ViewType.Bootstrap) => View("/Areas/Components/Views/PageSize/BootstrapView.cshtml", model),
                (false, ViewType.Plain) => View("/Areas/Components/Views/PageSize/PlainView.cshtml", model),
                (false, ViewType.TailwindAndDaisy) => View("/Areas/Components/Views/PageSize/Default.cshtml", model),
                _ => View("/Areas/Components/Views/PageSize/Default.cshtml", model)
            };

            // If the view exists in the app, use it; otherwise, use the fallback RCL view
        }
        /// <summary>
        /// Checks if a view exists in the consuming application.
        /// </summary>

        private bool ViewExists(string viewName)
        {
            var result = ViewEngine.FindView(ViewContext, viewName, false);
            return result.Success;
        }
    }
```

# El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Well that's it for the PageSize tag helper . I'm still working on the documentation and examples but the code is gettign better dailyM SK3