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
A Paging-View-Komponente ASPA.-NET Core Tag Helper MSA(-Part Arta Arta ...-A Flippy Tag Helper MSA
Monday, 17 March 2025
Einleitung
Während ich ein Projekt aufbaute, baute ich die Paging Tag Helfer für Ich cam auch über ANOTHER need-Ein Weg, um leicht zu bauen Sortierfunktionalität für eine Tabelle der Ergebnisse mit HtMX-Unterstützung
So..... Ich präsentiere Ihnen die Flippy Tisch Kopf-Tag Helfer thingy. Dies ist nur ein schneller Artikel mit einer LOT von code-. die Proben hierUnd der Quellcode wie immer ist hier.
I'-m ziemlich schreckliche Gebäudebeispiele (-it-'-s ein Slog innit-)- aber IMSC4-ll versuchen, mehr und mehr hinzuzufügen, während ich entlang gehe MSC5- Zum Installieren von :
dotnet add package mostlylucid.pagingtaghelper

Der Flippy Tag Helfer
Also, was ist dieses Ding??? Nun in Kürze es .'?s eine Möglichkeit, einen Tabellenkopf zu generieren . (... oder irgendwo anders wirklich ..)... was Ihnen erlaubt, eine Tabelle der Ergebnisse zu sortieren .
An ihm lassen Sie das einfachste und ohne HTMX-Integration tun.
<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>
Hier sehen Sie den Spaltennamen, den Test für den Header und einen Platz zum Zurückgeben von -(. JetBrains-.-Annotationen welche unter vielen anderen Sachen, die I']ve kaum zerkratzten die Oberfläche des Gebens intellisense für den Controller und Aktion Namen
Dies wird einen Link erzeugen, der zurück zu den PageSortTagHelperNoHtmx Maßnahmen im Bereich der Home Controller mit dem Spaltennamen und der aktuellen Sortierreihenfolge sowie allen anderen Parametern in der URL auto-append-querystring Attribute: . , Dies ermöglicht es Ihnen leicht einen nützlichen Postback Link zu erhalten., , Sie sehen unten Ich machte es ziemlich flexibel, wo Sie href , / , Aktion , & , Controller und bekommen einen Link zurück mit den Querystring-Parametern angehängt
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);
}
Dies ist die Methode, die die Querystring-Parameter an die URL anhängt. Controller und Action Attribute oder wenn Sie die href Attribut, das es verwendet, wenn Sie setzen AutoAppend zu false es wird nur die href Attribut, wie es ( ist Mittel können Sie Ihre eigene für spezifische Verwendung rollen
Verrückte Konfig
Um dies so nützlich wie möglich zu machen, ohne HTMX®.. Mit -/. ohne Tailwind. - &. DaisyUI usw. mit IM'.ve können Sie diese relativ einfache Steuerung konfigurieren.
[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; }
Sie können hier sehen, ich habe Eigenschaften für PRETTY MUCH alles in der Steuerung.(-I'-m nicht gehen, um durch sie alle hier zu gehen, sollten sie ziemlich selbst sein...
Mit HTMX
Nun, wie Sie vielleicht gelernt haben I' hm ein HTMX NUT so wie üblich unterstützt diese HTX ziemlich nahtlos
Es ENDGÜLT, use-htmx true, die in der hx-vals zusammen mit diesem verwende ich die HTMX-Taghilfer um den Code so einfach wie möglich zu machen
<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>
Und nun, dass ' ist es reallty nur Arbeiten mit HTMX nahtlos
Der MVC-Controller
Ich poste dies dann zurück zu einem einfachen MVC-Controller, der einige Beispieldaten erzeugt.
[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;
}
Das OrderByField Erweiterungsverfahren
Oh und ich habe eine kleine Erweiterungsmethode verpackt, in der ein benanntes Bestellfeld auf die Daten angewendet wird. IQueryable Sie können sehen, dass dies eine Methode für starke getippte Spaltennamen und eine für IQueryable String-Spaltennamen hat.
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";
}
Schlussfolgerung
Dass-''s wirklich es -.'s gerade etwas, das ich brauchte, also baute ich es ein stecken im Nuget Paket -MSC3's