# A Paging View Component ASP.NET Κορυφαία Ετικέτα Βοηθός (Προσπαθήστε ΜSK2 PageSizeM SK3

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

<!--category-- ASP.NET Core, TagHelper, PagingTagHelper -->
# Εισαγωγή

Ως μέρος της συνεχιζόμενης γνώσης μου με τον βοηθό tag paging έχω τώρα χωρίσει το PageSize στο δικό του βοηθός tag. Αυτό είναι για να κάνει τον βοηθό tag πιο ευέλικτο και να επιτρέψει πιο πολύπλοκα σενάρια paging.

**Ξανά; αυτά είναι όλα τα έργαΜSK1 διατηρεί την πρόοδο. μου αρέσει' ήδη [ΕΠΙΧΕΙΡΕΥΣΗ 0.9.0](https://www.nuget.org/packages/mostlylucid.pagingtaghelper#readme-body-tab) κατά τη στιγμή της συγγραφής, αλλά ΧΡΗΣΙΜΑΣΤΕ ΣΤΟ ΔΙΚΑΙΟ ΤΟΥΣ RISK. Αυτό' είναι δωρεάν αλλά ΔΕΝ YET ποιότητας απελευθέρωσης IMHO M SK2I donMK3 ακόμη και να έχουν [πλήρες δείκτες](https://taghelpersample.mostlylucid.net/PageSizeWithHTMX) Προς το παρόν, ανακοινώθηκε**

Δείτε το [Κωδικός για την ετικέτα βοηθός εδώ](https://github.com/scottgal/mostlylucid.pagingtaghelper/blob/main/mostlylucid.pagingtaghelper/Components/PageSizeTagHelper.cs).

[TOC]

# Το μέγεθος της σελίδας Tag Helper

Όπως και με τους άλλους βοηθούς ετικέτας μου, αυτό είναι σχεδιασμένο για χρήση-καλύπτω συχνά πώς να αλλάξετε εύκολα το μέγεθος της σελίδας σε λίστες αποτελεσμάτωνΜSK2
Από την πρώτη ματιά αυτό φαίνεται αρκετά απλό, αλλά μπορεί να γίνει TRICKY όταν προσθέσετε HTMX στο μείγμα.

## Απαιτήσεις

Έτσι, για την περίπτωση χρήσης μου οι απαιτήσεις ήταν οι ακόλουθες

1. Επιτρέπει εύκολη χρήση για τον προγραμματιστή τσαγιού Το ( μπορεί να ρυθμιστεί όσο το δυνατόν περισσότερο χρησιμοποιώντας ένα απλό μοντέλο σελίδας το ) μπορεί
2. Υποστηρίζει την ενημέρωση του μεγέθους της σελίδας χρησιμοποιώντας το HTMX - που σημαίνει ότι το ' μπορεί να δημοσιεύσει πίσω *Διατηρώντας όλες τις παραμέτρους της ζώνης ερωτήσεων*.
   Αυτό είναι σημαντικό, καθώς επιτρέπει στον χρήστη να αλλάξει το μέγεθος της σελίδας χωρίς να χάσει οποιαδήποτε άλλα φίλτρα έχουν εφαρμοστεί.
3. Έχει μια κάπως δυναμική ποικιλία των μεγεθών σελίδων (συμπεριλαμβανομένου ενός ΜSK1Όλα'ΕπιλογήM SK3

## Η Tag Helper

Με αυτό το σκεπτικό σχεδιάζω την TagHelper για να είναι η ακόλουθη:

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

Έτσι, σε αυτή την περίπτωση ξανά παίρνει το πρότυπο μοντέλο σελίδας μου:

```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; }
}
```

Που παρέχει την `Page`, `TotalItems`, `PageSize`, `ViewType` και `LinkUrl` Για την ιστοσελίδα.

Μπορείτε επίσης να προσδιορίσετε αυτά με τη μορφή χαρακτηριστικών του μοντέλου:

```html

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

Που είναι η `hx-target` Εδώ είναι ο στόχος για την αίτηση HTMX και το `hx-swap` Είναι ο στόχος για την απάντηση.

Σε αυτή την περίπτωση, το ' αναφέρει ότι θα χρησιμοποιήσει τον καθορισμένο `PageSize` και `TotalItems` και `ViewType` Για να γίνει ο επιλέκτης μεγέθους σελίδας.

## Τζέις Σένιανγκανς

Δυστυχώς αυτή τη φορά γύρω μου είχα μια απαίτηση που θα μπορούσα να αποκτήσω' θα χειριστεί καθαρά την πλευρά του διακομιστή. θα ήθελα να διατηρήσω τις παραμέτρους της γραμμής ερωτήσεων στο αίτημα αλλαγής μεγέθους σελίδων
Για να το πετύχω αυτό έχω δύο κομμάτια του JS που θα φορτώσει στον έλεγχο ανάλογα με την HTMX χρησιμοποιείται ούρο όχι

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

Εάν χρησιμοποιείται το HTMX, αυτό επιστρέφει στη σελίδα:

```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;
})();
```

Αυτό είναι ένα απλό κομμάτι του JavaScript που θα εισάγει τις τρέχουσες παραμέτρους ερωτηματολόγησης στο αίτημα.

Εναλλακτικά, εάν η HTMX ΔΕΝ είναι ενεργοποιημένη θα κάνει αυτό:

```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();
    }
})();
```

Μια αλλαγή που θα κάνω με το ' αναμένεται να είναι η αλλαγή του ονόματος του ' αναμετρητή ΜSK2 αναμετρική `__pageSizeListenerAttached ` Δεδομένου ότι το ' διατηρεί λίγο πολύ γενικό και το χρησιμοποιώ τόσο για HTMX όσο και για μη-- με τις αιτήσεις του .

## Ο Κώδικας

Ο ίδιος ο βοηθός ετικέτας είναι αρκετά απλός, ο κύριος κώδικας απλά κατοικεί μερικές ιδιότητες και στη συνέχεια παραδίδει την προβολή ΣυνιστώσαΜSK1

```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>"
            );
        }
    }
```

Ένα σκληρό κομμάτι είναι να χειριστεί σωστά το `PageSizeSteps` atribute. Αυτό είναι ένα comma ξεχωριστό κατάλογο των μεγεθών σελίδων που ο χρήστης μπορεί να επιλέξει απόΜSK1 OR μπορείτε να περάσετε στα δικά σας βήματα. Μπορείτε επίσης να προσδιορίσετε το MaxPageSize το οποίο είναι το μέγιστο μέγεθος της σελίδας που μπορούν να επιλεγούν

```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;
    }

```

Όπως και με το άλλο βοηθό ετικετών μου, χρησιμοποιώ το πρότυπο TagHelper->ViewComponent για να κάνω την προβολή συνιστώμενη στο .Αυτό μου επιτρέπει να κρατήσω τη συμβολή ετικέτας απλή και το σύνθετο στοιχείο προβολής στην .Είναι επίσης δυνατόν να αλλάξετε τις απόψεις για διαφορετικές εμπειρίες στην ; ή ακόμη και να ενέσετε τη δική σας εικόνα και χρήση που η (χρησιμοποιεί `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;
        }
    }
```

# Στο συμπέρασμα

Λοιπόν, ίσως το ' το γνωρίζει για τον βοηθό ετικέτας PageSize. ίσως το γνωρίζω' μπορεί ακόμα να δουλεύει πάνω στην τεκμηρίωση και τα παραδείγματα αλλά ο κώδικας είναι καλύτερος καθημερινά