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

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 Core PagingTagHelper TagHelper

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

Tuesday, 25 March 2025

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. 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. 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í.

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:

<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:

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:


<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

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

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

(() => {
    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:

(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.

 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 .

    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).


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

logo

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