A Paging View Component ASP.NET Core Tag Helper (Part 1 the Bare-Bones) ( ελληνικά (Greek_)

A Paging View Component ASP.NET Core Tag Helper (Part 1 the Bare-Bones)

Comments

NOTE: Apart from English (and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.

Tuesday, 11 March 2025

//

Less than a minute

Εισαγωγή

Ένα πρόγραμμα εργασίας τις προάλλες απαιτούσε την εφαρμογή των αποτελεσμάτων της φόρμας τηλεειδοποίησης. Ο βοηθός μου ήταν πάντα... Βοηθός ετικετών από τον Darrel O'Neil όπως έγραψα για Ορίστε. Παρ' όλα αυτά, για οποιονδήποτε λόγο είναι απλά... Σταμάτησε η εργασία Για μένα. Έτσι, αντί να προσπαθώ να ξεγελάσω μέσα από αυτό που μοιάζει με ένα εγκαταλελειμμένο έργο σε αυτό το σημείο αποφάσισα να φτιάξω ένα μόνος μου.

Ως συνήθως, μπορείς να βρεις την πηγή γι' αυτό. στο GitHub μου

Έχω ένα site δειγμάτων για αυτό το έργο Φιλοξένησε εδώ.

Αυτό έχει δείγματα της εξόδου όπως αυτό:

Αναζήτηση επίδειξης με ετικέτα helperg

Απαιτήσεις

Για αυτόν τον βοηθό ετικετών είχα μερικές απαιτήσεις:

  1. Θα πρέπει να λειτουργεί απρόσκοπτα με Ανεμοστρόβιλος και DaisyUI; προτιμώ CSS πλαίσια μου.
  2. Θα πρέπει να συνεργαστεί με HTMX χωρίς να προκαλέσει προβλήματα.
  3. Θα πρέπει να έχετε μια πτώση μεγέθους σελίδων που χρησιμοποιεί HTMX για να αναποδογυρίσει (έτσι, αν δεν χρησιμοποιήσετε HTMX θα πρέπει ακόμα να λειτουργήσει, αλλά θα πρέπει να προσθέσετε ένα κουμπί).
  4. Θα πρέπει να είναι εύκολο να ρυθμίσετε και να χρησιμοποιήσετε
    1. Αποδέχεται ένα μοντέλο επιγραφής έτσι ώστε να είναι απλό στη χρήση σε μια σελίδα Razor
    2. Θα πρέπει να είναι σε θέση να διαμορφωθεί με μερικές απλές παραμέτρους
  5. Θα πρέπει να είναι ένα πακέτο Nuget έτσι ώστε όλοι εσείς οι μεγάλοι άνθρωποι μπορεί να παίξει με αυτό.
  6. Θα πρέπει να είναι και τα δύο ένα ViewComponent και ένα TagHelper έτσι ώστε να μπορεί να χρησιμοποιηθεί και στις σελίδες και τις απόψεις Razor? με αυτό κατά νου θα πρέπει επίσης να έχουν μια υπερβάσιμη Dafault.cshtml Θέα.
  7. Λειτουργεί με μια απλή λειτουργία αναζήτησης.

Στο μέλλον θα προσθέσω την ικανότητα:

  1. Προσθέστε έθιμο CSS για να αποφύγετε να συνδεθείτε με DaisyUI και Tailwind~~ Έχω προσθέσει αυτή την ικανότητα ήδη δείτε το demo εδώ: https://taghelperδείγμα.mostlyclearning.net/Home/PlainView
  2. Η ικανότητα καθορισμού μεγεθών σελίδων
  3. Η ικανότητα να προσθέσετε μια προσαρμοσμένη κλήση JS στο μέγεθος της σελίδας πτώση (για να σας επιτρέψει να ΜΗ χρησιμοποιείτε HTMX).
  4. Χρησιμοποιήστε Alpine για να κάνετε τον βομβητή πιο ενεργό και ανταποκρίνεται (όπως έκανα στο προηγούμενο άρθρο).

Εγκατάσταση

Το taghelper είναι τώρα ένα γυαλιστερό νέο πακέτο Nuget ώστε να μπορείτε να το εγκαταστήσετε με την ακόλουθη εντολή:

dotnet add package mostlylucid.pagingtaghelper

Στη συνέχεια, θα προσθέσετε τον βοηθό ετικετών στο δικό σας _ViewImports.cshtml αρχείο όπως:

@addTagHelper *, mostlylucid.pagingtaghelper

Τότε μπορείτε απλά να αρχίσετε να το χρησιμοποιείτε? Παρέχω μερικά μαθήματα βοηθών που μπορείτε να χρησιμοποιήσετε για να το configre όπως

IPagingModel

Αυτό είναι το "βασικό υλικό" που πρέπει να ξεκινήσεις. Είναι μια απλή διεπαφή που μπορείτε να εφαρμόσετε στο μοντέλο σας για να κάνετε την κλήση να λειτουργήσει. Σημειώστε ότι ViewType είναι προαιρετική εδώ προεπιλεγεί να TailwindANdDaisy Αλλά μπορείτε να το ρυθμίσετε Custom, Plain ή Bootstrap αν θέλετε να χρησιμοποιήσετε μια διαφορετική άποψη.

public enum ViewType
{
TailwindANdDaisy,
Custom,
Plain,
Bootstrap
}

Ή μπορείτε ακόμη και να καθορίσετε μια προσαρμοσμένη προβολή με τη χρήση του TagHelper's use-local-view ιδιοκτησία.

namespace mostlylucid.pagingtaghelper.Models;

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; }
}
namespace mostlylucid.pagingtaghelper.Models;

public interface IPagingSearchModel : IPagingModel
{
    public string? SearchTerm { get; set; }
}

Έχω επίσης θέσει σε εφαρμογή αυτά στο πρόγραμμα για την παροχή μιας βάσης:

public abstract class BasePagerModel : IPagingModel
{
    public int Page { get; set; } = 1;
    public int TotalItems { get; set; } = 0;
    public int PageSize { get; set; } = 10;
    public ViewType ViewType { get; set; } = ViewType.TailwindANdDaisy;

    public string LinkUrl { get; set; } = "";

}
public abstract class BasePagerSearchMdodel : BasePagerModel, IPagingSearchModel
{
    public string? SearchTerm { get; set; }
}

Θα καλύψω τη λειτουργία αναζήτησης σε ένα μελλοντικό άρθρο..

Το TagHelper

Στη συνέχεια, σκέφτηκα πώς θα ήθελα το TagHelper να μοιάζει σε χρήση:

<paging
    x-ref="pager"
    hx-boost="true"
    hx-indicator="#loading-modal"
    hx-target="#user-list "
    hx-swap="show:none"
    model="Model"
    pages-to-display="10"
    hx-headers='{"pagerequest": "true"}'>
</paging>

Εδώ μπορείτε να δείτε ότι ορίζω μερικές παραμέτρους HTMX και το μοντέλο που πρέπει να χρησιμοποιήσετε για την επιγραφή. Επίσης ορίζω τον αριθμό των σελίδων για να εμφανίσετε και τις κεφαλίδες για να στείλετε με το αίτημα (αυτό μου επιτρέπει να χρησιμοποιήσω HTMX για να κατοικήσει τη σελίδα).

Το συστατικό έχει επίσης ένα BUNCH από άλλα στοιχεία config τα οποία θα δουλέψω σε μελλοντικά άρθρα. Όπως μπορείτε να δείτε theerer apos? S μια Πολλή πιθανή διαμόρφωση.

<paging css-class=""
        first-last-navigation=""
        first-page-text=""
        next-page-aria-label=""
        next-page-text=""
        page=""
        pages-to-display=""
        page-size=""
        previous-page-text=""
        search-term=""
        skip-forward-back-navigation=""
        skip-back-text=""
        skip-forward-text="true"
        total-items=""
        link-url=""
        last-page-text=""
        show-pagesize=""
        use-htmx=""
        use-local-view=""
        view-type="Bootstrap"
        htmx-target=""
        id=""
></paging>

Το TagHelper είναι αρκετά απλό αλλά έχει ένα μάτσο ιδιότητες που επιτρέπουν στο χρήστη να προσαρμόσετε τη συμπεριφορά (μπορείτε να δείτε αυτό παρακάτω στο προβολή@ title: window ) εκτός από τις ιδιότητες (τις οποίες δεν θα επικολλήσω εδώ για συντομία) ο κώδικας είναι αρκετά απλός:

    /// <summary>
    /// Processes the tag helper to generate the pagination component.
    /// </summary>

    /// <param name="context">The tag helper context.</param>
    /// <param name="output">The tag helper output.</param>
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
   
        output.TagName = "div";
        
        //Remove all the properties that are not needed for the rendered content.
        output.Attributes.RemoveAll("page");
        output.Attributes.RemoveAll("link-url");
        output.Attributes.RemoveAll("page-size");
        output.Attributes.RemoveAll("total-items");
        output.Attributes.RemoveAll("pages-to-display");
        output.Attributes.RemoveAll("css-class");
        output.Attributes.RemoveAll("first-page-text");
        output.Attributes.RemoveAll("previous-page-text");
        output.Attributes.RemoveAll("skip-back-text");
        output.Attributes.RemoveAll("skip-forward-text");
        output.Attributes.RemoveAll("next-page-text");
        output.Attributes.RemoveAll("next-page-aria-label");
        output.Attributes.RemoveAll("last-page-text");
        output.Attributes.RemoveAll("first-last-navigation");
        output.Attributes.RemoveAll("skip-forward-back-navigation");
        output.Attributes.RemoveAll("model");
        output.Attributes.RemoveAll("show-pagesize");
        output.Attributes.RemoveAll("pagingmodel");
        output.Attributes.RemoveAll("use-local-view");
        
        var pagerId =  PagerId ?? $"pager-{Guid.NewGuid():N}";
        var linkUrl = LinkUrl ?? ViewContext.HttpContext.Request.Path;
        PageSize = Model?.PageSize ?? PageSize ?? 10;
        Page = Model?.Page ?? Page ?? 1;
        ViewType = Model?.ViewType ?? ViewType;
        TotalItems = Model?.TotalItems ?? TotalItems ?? 0;
        if(Model is IPagingSearchModel searchModel)
            SearchTerm = searchModel?.SearchTerm ?? SearchTerm ?? "";
        output.Attributes.SetAttribute("id", pagerId);
        var viewComponentHelper = (IViewComponentHelper)ViewContext.HttpContext.RequestServices.GetService(typeof(IViewComponentHelper))!;
        ((IViewContextAware)viewComponentHelper).Contextualize(ViewContext);

        var pagerModel = PagerModel ?? new PagerViewModel()
        {
            
            ViewType = ViewType,
            UseLocalView = UseLocalView,
            UseHtmx = UseHtmx,
            PagerId = pagerId,
            SearchTerm = SearchTerm,
            ShowPageSize = ShowPageSize,
            Model = Model,
            LinkUrl = linkUrl,
            Page = Page,
            PageSize = PageSize,
            TotalItems = TotalItems,
            PagesToDisplay = PagesToDisplay,
            CssClass = CssClass,
            FirstPageText = FirstPageText,
            PreviousPageText = PreviousPageText,
            SkipBackText = SkipBackText,
            SkipForwardText = SkipForwardText,
            NextPageText = NextPageText,
            NextPageAriaLabel = NextPageAriaLabel,
            LastPageText = LastPageText,
            FirstLastNavigation = FirstLastNavigation,
            SkipForwardBackNavigation = SkipForwardBackNavigation,
            HtmxTarget = HtmxTarget,
            
        };

        var result = await viewComponentHelper.InvokeAsync("Pager", pagerModel);
        output.Content.SetHtmlContent(result);
    }

Περιλαμβάνει τα εξής βήματα:

  1. Ορισμός του ονόματος ετικέτας εξόδου σε div; αυτό είναι το δοχείο για τον βομβητή.
  2. Απομακρύνετε όλες τις ιδιότητες που δεν χρειάζονται για το περιεχόμενο (αλλά αφήστε όλα τα παρεχόμενα στο χρήστη, αυτό επιτρέπει την απλή προσαρμογή).
  3. Ορίστε τον βομβητή σε ένα τυχαίο GUID αν δεν παρέχεται (αυτό πραγματικά χρησιμοποιείται για προσαρμοσμένο κωδικό, μπορείτε να καθορίσετε την ταυτότητα ή απλά αφήστε αυτόν τον κωδικό να το φροντίσει).
  4. Ορίστε το linkUrl στην τρέχουσα διαδρομή αν δεν παρέχεται - αυτό σας επιτρέπει να το παρακάμψετε αν θέλετε να χρησιμοποιήσετε ένα διαφορετικό URL.
  5. Ορισμός της σελίδαςΜέγεθος, σελίδα, ViewType, TotalItems και SearchTerm στο μοντέλο εάν παρέχεται ή εάν δεν προεπιλεγεί. Αυτό μας επιτρέπει απλά να περάσουμε στο IPagingModel και να έχουν το έργο βομβητή χωρίς περαιτέρω διαμόρφωση.
  6. Ορίστε το χαρακτηριστικό ταυτότητας στο βομβητή.
  7. Αποκτήστε το ViewComponentHelper από το δοχείο DI και περιγράψτε το με το τρέχον ViewContext.
  8. Δημιουργία ενός νέου PagerViewModel με τις ιδιότητες που καθορίζονται στις τιμές που έχουμε ή τις προεπιλογές αν δεν παρέχονται.
  9. Εισάγετε το Pager ΠροβολήComponent με το PagerViewModel και να ρυθμίσετε το περιεχόμενο εξόδου στο αποτέλεσμα.

Πάλι όλα πολύ απλά.

Η ΠροβολήComponent

Η Άποψη

Η θέα για το ViewComponent είναι αρκετά απλή.Είναι απλά ένας βρόχος μέσα από τις σελίδες και μερικές συνδέσεις με την πρώτη, τελευταία, επόμενη και προηγούμενες σελίδες.

Complete source code for the Default TailwindUI & Daisy view
@model mostlylucid.pagingtaghelper.Components.PagerViewModel
@{
    var totalPages = (int)Math.Ceiling((double)Model.TotalItems! / (double)Model.PageSize!);
    var pageSizes = new List<int>();
    if (Model.ShowPageSize)
    {
        // Build a dynamic list of page sizes.

        // Fixed steps as a starting point.
        int[] fixedSteps = { 10, 25, 50, 75, 100, 125, 150, 200, 250, 500, 1000 };

        // Add only those fixed steps that are less than or equal to TotalItems.
        foreach (var step in fixedSteps)
        {
            if (step <= Model.TotalItems)
            {
                pageSizes.Add(step);
            }
        }

        // If TotalItems is greater than the largest fixed step,
        // add additional steps by doubling until reaching TotalItems.
        if (Model.TotalItems > fixedSteps.Last())
        {
            int next = fixedSteps.Last();
            while (next < Model.TotalItems)
            {
                next *= 2;
                // Only add if it doesn't exceed TotalItems.
                if (next < Model.TotalItems)
                {
                    pageSizes.Add(next);
                }
            }

            // Always include the actual TotalItems as the maximum option.
            if (!pageSizes.Contains(Model.TotalItems.Value))
            {
                pageSizes.Add(Model.TotalItems.Value);
            }
        }
    }
}
@if (totalPages > 1)
{
    <div class="@Model.CssClass flex items-center justify-center" id="pager-container">
        @if (Model.ShowPageSize)
        {
            var pagerId = Model.PagerId;
            var htmxAttributes = Model.UseHtmx
                ? $"hx-get=\"{Model.LinkUrl}\" hx-trigger=\"change\" hx-include=\"#{pagerId} [name='page'], #{pagerId} [name='search']\" hx-push-url=\"true\""
                : "";


            <!-- Preserve current page -->
            <input type="hidden" name="page" value="@Model.Page"/>
            <input type="hidden" name="search" value="@Model.SearchTerm"/>
            <input type="hidden" class="useHtmx" value="@Model.UseHtmx.ToString().ToLowerInvariant()"/>
            if (!Model.UseHtmx)
            {
                <input type="hidden" class="linkUrl" value="@Model.LinkUrl"/>
            }

            <!-- Page size select with label -->
            <div class="flex items-center mr-8">
                <label for="pageSize-@pagerId" class="text-sm text-gray-600 mr-2">Page size:</label>
                <select id="pageSize-@pagerId"
                        name="pageSize"
                        class="border rounded select select-primary select-sm pt-0 mt-0 min-w-[80px] pr-4"
                        @Html.Raw(htmxAttributes)>
                    @foreach (var option in pageSizes.ToList())
                    {
                        var optionString = option.ToString();
                        if (option == Model.PageSize)
                        {
                            <option value="@optionString" selected="selected">@optionString</option>
                        }
                        else
                        {
                            <option value="@optionString">@optionString</option>
                        }
                    }
                </select>
            </div>
        }

        @* "First" page link *@
        @if (Model.FirstLastNavigation && Model.Page > 1)
        {
            var href = $"{Model.LinkUrl}?page=1&pageSize={Model.PageSize}";
            if (!string.IsNullOrEmpty(Model.SearchTerm))
            {
                href += $"&search={Model.SearchTerm}";
            }

            <a class="btn btn-sm"
               href="@href">
                @Model.FirstPageText
            </a>
        }

        @* "Previous" page link *@
        @if (Model.Page > 1)
        {
            var href = $"{Model.LinkUrl}?page={Model.Page - 1}&pageSize={Model.PageSize}";
            if (!string.IsNullOrEmpty(Model.SearchTerm))
            {
                href += $"&search={Model.SearchTerm}";
            }

            <a class="btn btn-sm"
               href="@href">
                @Model.PreviousPageText
            </a>
        }

        @* Optional skip back indicator *@
        @if (Model.SkipForwardBackNavigation && Model.Page > Model.PagesToDisplay)
        {
            <a class="btn btn-sm btn-disabled">
                @Model.SkipBackText
            </a>
        }

        @* Determine visible page range *@
        @{
            int halfDisplay = Model.PagesToDisplay / 2;
            int startPage = Math.Max(1, Model.Page.Value - halfDisplay);
            int endPage = Math.Min(totalPages, startPage + Model.PagesToDisplay - 1);
            startPage = Math.Max(1, endPage - Model.PagesToDisplay + 1);
        }
        @for (int i = startPage; i <= endPage; i++)
        {
            var href = $"{Model.LinkUrl}?page={i}&pageSize={Model.PageSize}";
            if (!string.IsNullOrEmpty(Model.SearchTerm))
            {
                href += $"&search={Model.SearchTerm}";
            }

            <a data-page="@i" class="btn btn-sm mr-2 @(i == Model.Page ? "btn-active" : "")"
               href="@href">
                @i
            </a>
        }

        @* Optional skip forward indicator *@
        @if (Model.SkipForwardBackNavigation && Model.Page < totalPages - Model.PagesToDisplay + 1)
        {
            <a class="btn btn-sm btn-disabled mr-2">
                @Model.SkipForwardText
            </a>
        }

        @* "Next" page link *@
        @if (Model.Page < totalPages)
        {
            var href = $"{Model.LinkUrl}?page={Model.Page + 1}&pageSize={Model.PageSize}";
            if (!string.IsNullOrEmpty(Model.SearchTerm))
            {
                href += $"&search={Model.SearchTerm}";
            }

            <a class="btn btn-sm mr-2"
               href="@href"
               aria-label="@Model.NextPageAriaLabel">
                @Model.NextPageText
            </a>
        }

        @* "Last" page link *@
        @if (Model.FirstLastNavigation && Model.Page < totalPages)
        {
            var href = $"{Model.LinkUrl}?page={totalPages}&pageSize={Model.PageSize}";
            if (!string.IsNullOrEmpty(Model.SearchTerm))
            {
                href += $"&search={Model.SearchTerm}";
            }

            <a class="btn btn-sm"
               href="@href">
                @Model.LastPageText
            </a>
        }

        <!-- Page info text with left margin for separation -->
        <div class="text-sm text-neutral-500 ml-8">
            Page @Model.Page of @totalPages (Total items: @Model.TotalItems)
        </div>
    </div>
}
Αυτό είναι χωρισμένο σε μερικά τμήματα:
  1. Το μέγεθος της σελίδας dropdown
  2. Οι πρώτοι, τελευταίοι, επόμενοι και προηγούμενοι σύνδεσμοι
  3. Πήγαινε πίσω και πήγαινε προς τα εμπρός.
  4. Η σελίδα συνδέσμους
  5. Η σελίδα info text

Το μέγεθος σελίδας πτώση

Ένα πράγμα που μου έλειπε από τον αρχικό βοηθό ετικέτας ήταν μια πτώση μεγέθους σελίδας. Αυτή είναι μια απλή λίστα επιλογών, μπορείτε να δείτε Ξεκινώ αρχικά καθορίζοντας fixedSteps Τα οποία είναι μόνο μερικά σταθερά βήματα που θέλω να χρησιμοποιήσω για την πτώση. Στη συνέχεια, κάνω κύκλους μέσα από αυτά και τα προσθέτουμε στη λίστα. Μια συνήθεια που έχω πάντα είναι να έχω μια επιλογή "όλα" έτσι προσθέτω τα συνολικά στοιχεία στη λίστα αν δεν είναι ήδη εκεί.

@{
    var totalPages = (int)Math.Ceiling((double)Model.TotalItems! / (double)Model.PageSize!);
    var pageSizes = new List<int>();
    if (Model.ShowPageSize)
    {
        // Build a dynamic list of page sizes.

        // Fixed steps as a starting point.
        int[] fixedSteps = { 10, 25, 50, 75, 100, 125, 150, 200, 250, 500, 1000 };

        // Add only those fixed steps that are less than or equal to TotalItems.
        foreach (var step in fixedSteps)
        {
            if (step <= Model.TotalItems)
            {
                pageSizes.Add(step);
            }
        }

        // If TotalItems is greater than the largest fixed step,
        // add additional steps by doubling until reaching TotalItems.
        if (Model.TotalItems > fixedSteps.Last())
        {
            int next = fixedSteps.Last();
            while (next < Model.TotalItems)
            {
                next *= 2;
                // Only add if it doesn't exceed TotalItems.
                if (next < Model.TotalItems)
                {
                    pageSizes.Add(next);
                }
            }

            // Always include the actual TotalItems as the maximum option.
            if (!pageSizes.Contains(Model.TotalItems.Value))
            {
                pageSizes.Add(Model.TotalItems.Value);
            }
        }
    }
}

Στη συνέχεια, αποδίδω αυτό στη σελίδα

  @if (Model.ShowPageSize)
        {
            var pagerId = Model.PagerId;
            var htmxAttributes = Model.UseHtmx
                ? $"hx-get=\"{Model.LinkUrl}\" hx-trigger=\"change\" hx-include=\"#{pagerId} [name='page'], #{pagerId} [name='search']\" hx-push-url=\"true\""
                : "";


            <!-- Preserve current page -->
            <input type="hidden" name="page" value="@Model.Page"/>
            <input type="hidden" name="search" value="@Model.SearchTerm"/>
            <input type="hidden" class="useHtmx" value="@Model.UseHtmx.ToString().ToLowerInvariant()"/>
            if (!Model.UseHtmx)
            {
                <input type="hidden" class="linkUrl" value="@Model.LinkUrl"/>
            }

            <!-- Page size select with label -->
            <div class="flex items-center mr-8">
                <label for="pageSize-@pagerId" class="text-sm text-gray-600 mr-2">Page size:</label>
                <select id="pageSize-@pagerId"
                        name="pageSize"
                        class="border rounded select select-primary select-sm pt-0 mt-0 min-w-[80px] pr-4"
                        @Html.Raw(htmxAttributes)>
                    @foreach (var option in pageSizes.ToList())
                    {
                        var optionString = option.ToString();
                        if (option == Model.PageSize)
                        {
                            <option value="@optionString" selected="selected">@optionString</option>
                        }
                        else
                        {
                            <option value="@optionString">@optionString</option>
                        }
                    }
                </select>
            </div>
        }

Μπορείτε να δείτε ότι χρησιμοποιώ προαιρετικά κάποια χαρακτηριστικά HTMX για να μεταβιβάσω το μέγεθος της σελίδας στον διακομιστή και να ενημερώσω τη σελίδα διατηρώντας παράλληλα την τρέχουσα σελίδα και παράμετρο αναζήτησης (αν υπάρχει).

Εpiιpiλέον, εάν αpiοφασίσετε use-htmx=false ως παράμετρος στον βοηθό ετικετών δεν θα βγάλει αυτά, αλλά αντίθετα θα σας επιτρέψει να χρησιμοποιήσετε κάποια JS I παρέχει ως βοηθός HTML για να ενημερώσετε το μέγεθος της σελίδας.

@Html.PageSizeOnchangeSnippet()
    

Αυτό είναι ένα απλό σενάριο που θα ενημερώσει το μέγεθος της σελίδας και να ξαναφορτώσει τη σελίδα (σημειώστε ότι αυτό δεν λειτουργεί ακόμα για Plain CSS / Bootstrap όπως εγώ πρέπει να επεξεργαστεί τα ονόματα των ακινήτων κλπ).

document.addEventListener("DOMContentLoaded", function () {
    document.body.addEventListener("change", function (event) {
        const selectElement = event.target.closest("#pager-container select[name='pageSize']");
        if (!selectElement) return;

        const pagerContainer = selectElement.closest("#pager-container");
        const useHtmxInput = pagerContainer.querySelector("input.useHtmx");
        const useHtmx = useHtmxInput ? useHtmxInput.value === "true" : true; // default to true

        if (!useHtmx) {
            const pageInput = pagerContainer.querySelector("[name='page']");
            const searchInput = pagerContainer.querySelector("[name='search']");

            const page = pageInput ? pageInput.value : "1";
            const search = searchInput ? searchInput.value : "";
            const pageSize = selectElement.value;
            const linkUrl =  pagerContainer.querySelector("input.linkUrl").value ?? "";
            
            const url = new URL(linkUrl, window.location.origin);
            url.searchParams.set("page", page);
            url.searchParams.set("pageSize", pageSize);

            if (search) {
                url.searchParams.set("search", search);
            }

            window.location.href = url.toString();
        }
    });
});

Ενσωμάτωση HTMX

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

  • hx-boost="true" - Αυτό χρησιμοποιεί το ωραίο λειτουργία hx-boost να αναχαιτίσει το γεγονός κλικ και να στείλει το αίτημα μέσω HTMX.
  • hx-δείκτης="#loading-modal" - αυτό έχει μια μέθοδο φόρτωσης που θα δείξει κατά την επεξεργασία του αιτήματος.
  • hx-στόχος="#user-list" - αυτό είναι το στοιχείο που θα ανταλλάξει η απάντηση, σε αυτή την περίπτωση η λίστα χρηστών. Σημείωση: Αυτό περιλαμβάνει αυτή τη στιγμή το Pager για την απλότητα; μπορείτε να το κάνετε αυτό πιο ενεργό χρησιμοποιώντας Alpine (όπως στο δικό μου προηγούμενο άρθρο ) αλλά ήταν εκτός εμβέλειας αυτή τη φορά.
  • hx-swap="show:None"

Το Μοντέλο Φόρτωσης

Αυτό είναι αρκετά απλό και χρησιμοποιεί DaisyUI, boxicons και Tailwind για να δημιουργήσει μια απλή μέθοδο φόρτωσης.

<div id="loading-modal" class="modal htmx-indicator">
    <div class="modal-box flex flex-col items-center justify-center">
        <h2 class="text-lg font-semibold">Loading...</h2>
        <i class="bx bx-loader bx-spin text-3xl mt-2"></i>
    </div>
</div>

hx-δείκτης="#loading-modal" στη συνέχεια ορίζει ότι όταν μια αίτηση HTMX εκτελείται θα πρέπει να δείξει στη συνέχεια να κρύψει αυτή τη μέθοδο.

Μελλοντικά Χαρακτηριστικά

Αυτό είναι το πρώτο μέρος, προφανώς υπάρχουν πολλά περισσότερα να καλύψει και θα το κάνω σε μελλοντικά άρθρα; συμπεριλαμβανομένης της τοποθεσίας του δείγματος, εναλλακτικές απόψεις, τη λειτουργία αναζήτησης και το έθιμο CSS.

logo

©2024 Scott Galloway