Back to "مساعِد علِم رئيسي (الجزء 1-1، نوع ما... A Flippy Tagabger hander)"

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

مساعِد علِم رئيسي (الجزء 1-1، نوع ما... A Flippy Tagabger hander)

Monday, 17 March 2025

أولاً

لذا أثناء بناء مشروع بنيت أصلاً مُنْفِض مُنْفِف لـ أنا أيضاً أُراقبُ أيضاً خلال حاجة أخرى. طريقة لبناء خاصية الفرز بسهولة لجدول النتائج مع دعم HtMX.

إذاً... أقدم لكِ عنوان بطاقة (فليبي) المساعدة. هذه مجرد مقالة سريعة مع الكثير من الشفرة يمكنك أن ترى دائماً المعاينة هنا/ / / / والرمز المصدري كما هو دائماً هو هنا.

أنا في غاية الفظاعة أمثلة بناء (إنها عبارة عن عبارة (slog init)) لكنني سأحاول إضافة المزيد والمزيد كلما مضيت قدماً. إلى تثبيت:

dotnet add package mostlylucid.pagingtaghelper

Flippy Tagger

المُنَدِعِفِفِي مَزْدَدِكَةِ تَنْقِيجِي

فما هو هذا الشيء إذن؟ حسناً باختصار إنها طريقة لتوليد رأس جدول (أو في أي مكان آخر حقاً) والتي ستسمح لك بترتيب جدول النتائج.

في أبسط (وبدون دمج HTMX) يسمح لك بفعل هذا.

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

يمكنك أن ترى هنا أنك تحدد اسم العمود، اختبار الترويج، و المكان الذي يجب أن تُسند إليه (شكراً إلى 2 - البيانات الصادرة عن الهيئات الحكومية الدولية من بين العديد من الأشياء الأخرى، التي بالكاد خدشتها على سطح إعطاء المعلوماتية لأسماء المراقب المالي و Action.

هذا سيولّد رابطاً سيرجع إلى PageSortTagHelperNoHtmx اللجنة الدائمة Home متحكم مع اسم العمود واسم العمود والترتيب الحالي للنوع الترتيب وأي بارامترات أخرى في الواجهة (مراقب مع الـ auto-append-querystring ........... ،.............................................................................................................. هذا سوف يحصل بسهولة على وصلة خلفية مفيدة ، سوف ترونها في الأسفل ، لقد جعلتها مرنة جداً حيث يمكنك تحديد href/ action & controller و الحصول على وصلة إلى الخلف مع معاملات الإقتراح المُرفقة.

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

هذه هي الطريقة التي تُذيّل مُؤَمِّرات الإشارَة إلى العنوان. إنها بسيطة جداً، إنها تولد عنواناً مبنياً على Controller وقد عقد مؤتمراً بشأن Action أو إذا كنت قد وضعت href واصفتها أنها ستستخدم ذلك. إذا كنت قد وضعت AutoAppend إلى زوال سيستخدم فقط href صفة هو (معنى أنك يمكن أن لفة الخاصة بك لاستخدام حالات محددة).

(الشكل)

لجعل هذا مفيد بقدر الإمكان مع/بدون HTMX. مع / بدون Tayilwind & DizeUI وما إلى ذلك لقد أعطيتك مجموعة من الخصائص لتستخدمها لضبط هذه السيطرة البسيطة نسبياً

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

يمكنكم أن تروا هنا أن لدي خصائص لكل شيء في السيطرة (لن أمر بها جميعاً هنا يجب أن تكون تفسيرية ذاتية جداً).

مع HTMX

الآن كما قد تكون تعلمت أنا HTMX NUT لذا كالمعتاد هذا يدعم HTMX بشكل سلس جداً: يُؤكّد إلى: use-htmx الحقيقية التي تملأ في hx-vals ........... ،.............................................................................................................. مع هذا أنا استخدم مسهلات دعم و لجعل الرمز بسيطاً قدر الإمكان

            <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 المراقب المالي الذي يولد بعض البيانات العينة:

    [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 ألف مُنْت مُنْ مُنْ مُنْ مُنْ مُنْ مُنْ مُنْ مُنْ مُنْ مُنْمْتِلْمْ مِلْمِمْ مِنْ مِلْمِتْلِمْتِم مِلْمِلْتِم

ولدي طريقة تمديد صغيرة معبأة فيها تطبق حقل طلبية مسمّى على البيانات (أو) IQueryable وما إلى ذلك). وترد أدناه طريقة امتداد Tushhe Comple traditional orther. يمكنك أن ترى أن هذا له 3 طرق، واحد لأسماء الأعمدة المُحَبَّبة القوية، واحد لأسماء الأعمدة المتميِّزة، وواحد لأسماء الأعمدة المتسلسلة القابلة للقياس، وواحد لـ IEncommensions.

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

في الإستنتاج

هذا هو في الواقع عليه. إنه مجرد شيء كنت أحتاجه لذا بنيته وعلقته في حزمة النواة

logo

©2024 Scott Galloway