# A Paging View Component ASP.NET Βοηθός κεντρικής ετικέτας (Προσπαθήστε ΜSK2 ποικιλία κινούμενων ειδώνM SK3Α Flippy Tag HelperMK4

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

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

Έτσι, κατά τη δημιουργία ενός έργου που αρχικά κατασκευάστηκα [Ετικέτες Βοηθός για ](https://www.mostlylucid.net/blog/category/PagingTagHelper) Χρειάζομαι επίσης την κάμερα μέσω ANOTHER.Ένας τρόπος για να δημιουργήσετε εύκολα τη λειτουργικότητα ταξινόμησης για έναν πίνακα αποτελεσμάτων με υποστήριξη HtMX.

Έτσι ίσως το ... θα σας παρουσιάσει την ετικέτα Flippy Table Header help thingy.
Αυτό είναι απλά ένα γρήγορο άρθρο με πολλά κώδικα. Μπορείτε πάντα να δείτε [Τα δείγματα εδώ](https://taghelpersample.mostlylucid.net/). Και ο κώδικας πηγής όπως πάντα [Εδώ είναι](https://github.com/scottgal/mostlylucid.pagingtaghelper).

I'μ αρκετά τρομακτικά παραδείγματα κατασκευής ΜSK1μμ 'μ ένα σλόγκ innitμ ΜSK3μ αλλά μου αρέσει'μ θα προσπαθήσω να προσθέσω όλο και περισσότερο καθώς πηγαίνω κατά μήκος τωνμ.μ
Για να εγκαταστήσετε:

```bash
dotnet add package mostlylucid.pagingtaghelper
```

![Flippy TagΒοήθεια](flippyheaders.png?width=1000&format=webp)

[TOC]

# Το Flippy Tag Βοηθά

Έτσι τι είναι αυτό το πράγμα?Με λίγα λόγια,'έχει έναν τρόπο για να δημιουργήσει ένα κεφάλαιο του πίνακα MK2όπου αλλού πραγματικάM SK3 που θα σας επιτρέψει να ταξιδεύετε έναν πίνακας αποτελεσμάτων.

Σε αυτό' απλούστερη ΜSK1 και χωρίς HTMX ενσωμάτωσηM SK2 σας επιτρέπει να το κάνετε.

```html
    <sortable-header column="@nameof(FakeDataModel.CompanyCity)"
                             current-order-by="@Model.OrderBy"
                             descending="@Model.Descending"
                             controller="Home"
                             use-htmx="false"
                             action="PageSortTagHelperNoHtmx"
                       >@Html.DisplayNameFor(x => x.Data.First().CompanyCity)
</sortable-header>
```

Εδώ μπορείτε να δείτε να καθορίσετε το όνομα στήλης, την δοκιμή για τον επικεφαλής και ένα μέρος για να αναρτηθεί ΜSK1 ευχαριστώ [JetBrains.Ανακοίνωση](https://www.nuget.org/packages/Jetbrains.Annotations) το οποίο, μεταξύ πολλών άλλων πραγμάτων, αφορούσε τις εταιρείες , οι οποίες αφορούσα την ' θεωρούν ότι σπάνια διακόπηκε η επιφάνεια της παροχής νοημοσύνης για τους ελεγκτές και τα ονόματα δράσης που αφορούσαν τη ).

Αυτό θα δημιουργήσει ένα σύνδεσμο που θα δημοσιεύσει πίσω στο `PageSortTagHelperNoHtmx` δράση για την `Home` ελεγκτής με το όνομα στήλης και την τρέχουσα σειρά τύπου και οποιαδήποτε άλλη παράμετρος στο URL ( ελεγχόμενη από `auto-append-querystring` attribute. Αυτό σας επιτρέπει να πάρετε εύκολα ένα χρήσιμο σύνδεσμο επιστροφής, θα δείτε παρακάτω το έκανα αρκετά ευέλικτο όπου μπορείτε να προσδιορίσετε href ΜSK3 δράση ΜSK4 ελεγκτής και να πάρει μια σύνδεση πίσω με τις παράμετροι ερωτήσεων που προσαρμόζονται

```csharp
    private void AddQueryStringParameters(TagHelperOutput output, bool newDescending)
    {
        string? href = "";

        // If Controller and Action are provided, generate URL dynamically
        if (!string.IsNullOrEmpty(Controller) && !string.IsNullOrEmpty(Action))
        {
            href = Url.ActionLink(Action, Controller);
          
        }
        else if (output.Attributes.ContainsName("href")) // If href is manually set, use it
        {
            href = output.Attributes["href"].Value?.ToString() ?? "";
        }
        if(string.IsNullOrEmpty(href)) throw new ArgumentException("No href was provided or could be generated");
        
        // If AutoAppend is false or href is still empty, don't modify anything
        if (!AutoAppend && !string.IsNullOrWhiteSpace(href))
        {
            output.Attributes.RemoveAll("href");
            output.Attributes.SetAttribute("href", href);
            return;
        }

        // Parse the existing URL to append query parameters
        var queryStringBuilder = QueryString.Empty
            .Add("orderBy", Column)
            .Add("descending", newDescending.ToString().ToLowerInvariant());

        // Preserve existing query parameters from the current request
        foreach (var key in ViewContext.HttpContext.Request.Query.Keys)
        {
            var keyLower = key.ToLowerInvariant();
            if (keyLower != "orderby" && keyLower != "descending") // Avoid duplicating orderBy params
            {
             queryStringBuilder=   queryStringBuilder.Add(key, ViewContext.HttpContext.Request.Query[key]!);
            }
        }
href+= queryStringBuilder.ToString();
        
        // Remove old href and set the new one with the appended parameters
        output.Attributes.RemoveAll("href");
        output.Attributes.SetAttribute("href", href);
    }

```

Αυτή είναι η μέθοδος που προσαρμόζει τις παραμέτρους αναζήτησης στο URL. Το ' είναι αρκετά απλό, παράγει μια διεύθυνση URL με βάση το `Controller` και `Action` Αξιοθέατα ή αν σας αρέσει' διατηρεί τον `href` προσδιορίζει ότι θα χρησιμοποιήσει το . Αν σας αρέσει' `AutoAppend` Για να ψεύδεται, θα χρησιμοποιήσει μόνο το `href` χαρακτηριστικό όπως είναι (meanign μπορείτε να περιστρέψετε το δικό σας για συγκεκριμένη χρήση-casesM SK2

## Τρελός Config

Για να γίνει αυτό όσο το δυνατόν πιο χρήσιμο με / επιχειρήσεις χωρίς HTMX ΜSK1 επιχειρήσεις με 🎁/ επιχειρήματα χωρίς Tailwind ΜSK3 DaisyUI κλπ. Θα σας δώσω ένα BUNCH ιδιοτήτων για χρήση για τη διαμόρφωση αυτού του σχετικά απλού ελέγχου

```csharp
    [HtmlAttributeName("hx-controller")]
    [AspMvcController] // Enables IntelliSense for controller names
    public string? HXController { get; set; }

    [HtmlAttributeName("hx-action")]
    [AspMvcAction] // Enables IntelliSense for action names
    public string? HXAction { get; set; }
    
    
    [HtmlAttributeName("action")]
    [AspMvcAction]
    public string? Action { get; set; }
    
    [HtmlAttributeName("controller")]
    [AspMvcController]
    public string? Controller { get; set; }
    
    [ViewContext]
    [HtmlAttributeNotBound]
    public ViewContext ViewContext { get; set; }
    /// <summary>
    /// The column to sort by
    /// </summary>

    [HtmlAttributeName("column")] public string Column { get; set; } = string.Empty;

    /// <summary>
    /// Whether to auto-append any query string parameters
    /// </summary>

    [HtmlAttributeName("auto-append-querystring")] public bool AutoAppend { get; set; } = true;
    
    // <summary>
    /// Whether to use htmx ; specifcally used to set hx-vals
    /// </summary>

    [HtmlAttributeName("use-htmx")] public bool UseHtmx { get; set; } = true;

    /// <summary>
    /// The currently set order by column
    /// </summary>

    [HtmlAttributeName("current-order-by")]
    public string? CurrentOrderBy { get; set; }

    /// <summary>
    /// Sort direction, true for descending, false for ascending
    /// </summary>

    [HtmlAttributeName("descending")] public bool Descending { get; set; }

    
    /// <summary>
    ///  CSS class for the chevron up icon
    /// </summary>

    [HtmlAttributeName("chevron-up-class")]
    public string? ChevronUpClass { get; set; }
    
    /// <summary>
    ///  CSS class for the chevron down icon
    /// </summary>


    [HtmlAttributeName("chevron-down-class")]
    public string? ChevronDownClass { get; set; }
    
    /// <summary>
    /// The CSS class for the chevron when unsorted
    /// </summary>

    
    [HtmlAttributeName("chevron-unsorted-class")]
    public string? ChevronUnsortedClass { get; set; }

    /// <summary>
    /// The CSS class to use for the tag.
    /// </summary>

    [HtmlAttributeName("tag-class")] public string? TagClass { get; set; }
```

Μπορείτε να δείτε εδώ έχω ιδιότητες για PRETTY MUCH τα πάντα στον έλεγχο (I'm δεν πρόκειται να περάσουν όλα αυτά εδώ θα έπρεπε να είναι αρκετά αυτοπεποίθηση-explanatoryM SK3

# Με το HTMX

Τώρα που μπορεί να έχετε μάθει ότι αγαπώ το ' προσφέρει ένα HTMX NUT, έτσι όπως συνήθως αυτό υποστηρίζει το HTMx αρκετά αδιάβροχα προσφέρουν τα :.
Αποφασίζει να `use-htmx` Η αλήθεια που γεμίζει μέσα στο `hx-vals` Αξιοθέτηση. Μαζί με αυτό χρησιμοποιώ το[ HTMX Tag Βοηθός](https://github.com/khalidabuhakmeh/Htmx.Net/tree/bbc9de911a723bff7cd0884aa566ed68502fa94b) για να κάνει τον κωδικό όσο το δυνατόν απλούστερο.

```html
            <sortable-header column="Name"
                             current-order-by="@Model.OrderBy"
                             descending="@Model.Descending"
                             hx-get
                             hx-route-pagesize="@Model.PageSize"
                             hx-route-page="@Model.Page"
                             hx-route-search="@Model.SearchTerm"
                             hx-controller="Home"
                             hx-action="PageSortTagHelper"
                             hx-indicator="#loading-modal"
                             hx-target="#list"
                             hx-push-url="true">@Html.DisplayNameFor(x => x.Data.First().Name)
            </sortable-header>

```

Και καλά που το ' παραδέχεται ότι πραγματικά *Εργασίες* με την HTMX αδιαμφισβήτητα.

# Ο ελεγκτής MVC

Στη συνέχεια δημοσιεύω αυτό πίσω σε ένα απλό MVC Controller που παράγει μερικά δεδομένα δείγματος:

```csharp
    [Route("PageSortTagHelper")]
    public async Task<IActionResult> PageSortTagHelper(string? search, int pageSize = 10, int page = 1, string? orderBy = "", bool descending = false)
    {
        var pagingModel = await SortResults(search, pageSize, page, orderBy, descending);

        if (Request.IsHtmxBoosted() || Request.IsHtmx())
        {
            return PartialView("_PageSortTagHelper", pagingModel);
        }
        return View("PageSortTagHelper", pagingModel);
    }
    
     private async Task<OrderedPagingViewModel> SortResults(string? search, int pageSize, int page, string? orderBy, bool descending)
    {
        search = search?.Trim().ToLowerInvariant();
        var fakeModel = await dataFakerService.GenerateData(1000);
        var results = new List<FakeDataModel>();

        if (!string.IsNullOrEmpty(search))
            results = fakeModel.Where(x => x.Name.ToLowerInvariant().Contains(search)
                                           || x.Description.ToLowerInvariant().Contains(search) ||
                                           x.CompanyAddress.ToLowerInvariant().Contains(search)
                                           || x.CompanyEmail.ToLowerInvariant().Contains(search)
                                           || x.CompanyCity.ToLowerInvariant().Contains(search)
                                           || x.CompanyCountry.ToLowerInvariant().Contains(search)
                                           || x.CompanyPhone.ToLowerInvariant().Contains(search)).ToList();
        else
        {
            results = fakeModel.ToList();
        }

        if (!string.IsNullOrWhiteSpace(orderBy))
        {
            results = results.OrderByField(orderBy, descending).ToList();
        }

        var pagingModel = new OrderedPagingViewModel();
        pagingModel.TotalItems = results.Count();
        pagingModel.Page = page;
        pagingModel.SearchTerm = search;
        pagingModel.PageSize = pageSize;
        pagingModel.Data = results.Skip((page - 1) * pageSize).Take(pageSize).ToList();
        pagingModel.OrderBy = orderBy;
        pagingModel.Descending = descending;
        return pagingModel;
    }

```

## Η `OrderByField` Μέθοδος επέκτασης

Ω και έχω μια μικρή μέθοδο επέκτασης συσκευασμένη στην οποία εφαρμόζεται ένα ονομαστικό πεδίο παραγγελίας στα δεδομένα (or `IQueryable` κλπ).TΜSK1το σύνολο της μεθόδου επέκτασης είναι κάτω από. Μπορείτε να δείτε αυτό έχει M SK3 μέθοδοιMK4 ένα για ισχυρά ονόματα στήλης τύπου, έναν για IQueryable όνομα στήλη και έναν για IEnumerables

```csharp
using System.Linq.Expressions;
using System.Reflection;

namespace mostlylucid.pagingtaghelper.Extensions;

public static class QueryableExtensions
{
    public static IQueryable<T> OrderByField<T, TKey>(
        this IQueryable<T> source,
        Expression<Func<T, TKey>> keySelector,
        bool descending = false)
    {
        return descending ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
    }


    public static IQueryable<T> OrderByField<T>(
        this IQueryable<T> source,
        string sortBy,
        bool descending = false)
    {
        if (string.IsNullOrWhiteSpace(sortBy))
            return source; // No sorting applied

        var property =
            typeof(T).GetProperty(sortBy, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
        if (property == null)
            throw new ArgumentException($"Property '{sortBy}' not found on type '{typeof(T)}'.");

        var param = Expression.Parameter(typeof(T), "x");
        var propertyAccess = Expression.MakeMemberAccess(param, property);
        var lambda = Expression.Lambda(propertyAccess, param);

        var methodName = descending ? "OrderByDescending" : "OrderBy";

        var resultExpression = Expression.Call(
            typeof(Queryable),
            methodName,
            new[] { typeof(T), property.PropertyType },
            source.Expression,
            Expression.Quote(lambda)
        );

        return source.Provider.CreateQuery<T>(resultExpression);
    }
    
    public static IEnumerable<T> OrderByField<T>(
        this IEnumerable<T> source,
        string sortBy,
        bool descending = false)
    {
        var property = typeof(T).GetProperty(sortPropertyName(sortBy), BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
        if (property == null)
            throw new ArgumentException($"Property '{sortBy}' not found on type '{typeof(T)}'.");

        return descending
            ? source.OrderByDescending(x => property.GetValue(x, null))
            : source.OrderBy(x => property.GetValue(x, null));
    }

    // Helper methods for readability (optional)
    private static string sortPropertyName(string sortBy) => sortBy.Trim();
    private static string methodName(bool descending) => descending ? "OrderByDescending" : "OrderBy";
}
```

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

Αυτό που σίγουρα' αρέσει πραγματικά αυτό . Αρέσει το ' αρέει μόνο κάτι που χρειαζόμουν, οπότε το οικοδόμησα ένα κενό στο πακέτο καμπύλων για το ΜSK3