مركبة لرؤية التصفح ASP.NET مساعدة في كتابة العلامات الأساسية (جزء 2, حجم الصفحةM SK3
المقدمة
كجزء من مشواري المستمر مع مُساعدتي في البحث عن البطاقات الآن قمت بتقسيم PageSize إلى مَساعدتي الخاصة في البحث عنه.
مرة أخرى ; هذه كلها أعمال - تسير في تقدم إلى 0.9.0 في وقت الكتابة لكن إستخدموا في خطركم الخاص . إنه free but NOT YET of release quality العينات الكاملة حتى الآن!)
أنظر إلى الرمز لمساعد البطاقة هنا.
مساعدة ملصق حجم الصفحة
كما هو الحال مع مساعدي الأدوية الأخرى هذا مصمم لاستعمال على المدى الأول يبدو هذا بسيطاً إلى حد ما لكنه يمكن أن يصبح تريككي عندما تضيف HTMX إلى المزيج.
المتطلبات
لذا في حالتي المستخدمة كانت المتطلبات التالية
- يسمح باستعماله بسهولة للمطور (يمكن أن يتم ضبطه بأكبر قدر ممكن باستخدام نموذج صفحة بسيط)
- يدعم تحديث حجم الصفحة باستخدام HTMX - بمعنى أنها ' قادرة على إرسال مرة أخرى بينما يحتفظ كل مبادئ سطر الសំណួរ. هذا مهم لأنه يسمح للمستخدم بتغيير حجم الصفحة دون فقدان أي من المرشحات الأخرى التي أستخدموها.
- لديه مجموعة ديناميكية نوعا ما من الحجم للصفحات ( بما في ذلك إختيار 'كل'
مُساعد العلامة
مع هذا في عقلي قمت بتصميم TagHelper ليكون كالتالي:
<page-size
hx-target="#list"
hx-swap="innerHTML"
model="Model">
</page-size>
<div id="list">
<partial name="_ResultsList" model="Model"/>
</div>
لذا في هذه الحالة مرة أخرى تأخذ نموذجي القياسي
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 من أجل التصفح.
يمكنك أيضاً تحديد هذه في شكل صفات للنموذج:
<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 المستخدمة أو لا
@if (Model.UseHtmx)
{
@Html.HTMXPageSizeChange()
}
else
{
@Html.PageSizeOnchangeSnippet()
}
إذا كان يتم استخدام HTMX فإنه يحول هذا إلى صفحة:
(() => {
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;
})();
الذي هو قطعة بسيطة من جاواscript التي ستقوم بوضع параметрات سطر الاستفسار الحالي في الطلب.
وبدلاً من ذلك، إذا لم يتم تشغيل HTMX فإنه سينتج هذا
(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();
}
})();
أحد التغييرات التي سأقوم بها بكل جهد هو تغيير اسم المتغير __pageSizeListenerAttached لأنّه ' هو نوعاً ما أكثر تعقيداً و أنا أستخدمه لكلّ من HTMX و غيرها
الرمز
المساعد الذاتي هو بسيط جدا, الرمز الرئيسي يجمع فقط بعض الخاصيات ثم يعرض مكون الرؤية
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 attribute. هذا هو قائمة منفصلة عن البستة من حجم الصفحات التي يمكن للمستخدم أن يختارها من. أو يمكنك المرور بخطواتك الخاصةM SK2 يمكنك أيضاً تحديد MaxPageSize الذي هو حجم الصفحة الأعلى الذي يمكن إختياره
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;
}
كما هو الحال مع مساعدي الأدوية الأخرى، أستخدم نموذج مساعد الأدوة ->ViewComponent لإظهار مكونات الرؤية . هذا يسمح لي بجعل مساعد الأدوى بسيطاً وكون مكونات الرؤية معقداً | . | إنه أيضاً يسمح لك بتغيير المشاهد لتجارب مختلفة |; | أو حتى أن تضخ نظرتك الخاصة وتستخدم ذلك use-local-view).
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;
}
}
في الختام
حسناً ذلك ' إنه لمساعدة مدوّنة مقياس الصفحة . I' أنا مازلت أعمل على الوثائق و الأمثلة لكن الشفرة تتحسن كل يوم