英文:
ASP.NET Core: route parameter on a form submit action
问题
以下是要翻译的内容:
I have an Index
Razor page implemented with paging based on the tutorial here. I also implemented a user-selectable page sizing (details not shown here for brevity as it is not applicable to this problem) as well which I all have working. I am attempting to add the search functionality from the same page [here][2]. I have the search functionality working as well.
What I am struggling to get working is routing the current page size to the Get method when the search button is clicked.
Here is my search form from the index page:
<form asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize" method="get">
<div class="form-actions no-color">
<p>
Find by @nameof(Data.Audit.Log.Level):
<select asp-for="@Model.LevelFilter" asp-items="Html.GetEnumSelectList<Microsoft.Extensions.Logging.LogLevel>()">
<option selected="selected" value="@Model.LevelFilter">Please select</option>
</select>
<input type="submit" value="Search" class="btn btn-primary" asp-route-pageSize="@Model.Logs.PageSize" /> |
<a asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize">Back to full List</a>
</p>
</div>
</form>
And here is the Get
method on the Index
page:
public PaginatedList<Data.Audit.Log> Logs { get; set; }
public LogLevel? LevelFilter { get; set; }
public async Task OnGetAsync(int? pageIndex, int? pageSize, int? prevPageSize, LogLevel? levelFilter)
{
LevelFilter = levelFilter;
if (prevPageSize != null && pageSize != null && pageIndex != null && pageIndex > 1)
{
pageIndex = PaginatedList<Data.Audit.Log>.CalculateNewPageIndex(pageIndex.Value, prevPageSize.Value, pageSize.Value);
}
var query = DbContext.Logs.AsQueryable();
if(levelFilter != null)
{
query = query.Where(x => x.Level == levelFilter.ToString());
}
Logs = await PaginatedList<Data.Audit.Log>.CreateAsync(query.OrderByDescending(x => x.Logged).AsNoTracking(), pageIndex ?? 1, pageSize ?? _defaultPageSize);
}
As you can see in the .cshtml
markup, I have tried putting the asp-route-pageSize
on the form, input, and even the 'a' tag in the form. The html seems to be generated correctly:
[![enter image description here][3]][3]
The "Back for full List" behaves correctly and maintains the pageSize, however the Search button loses it. The only query parameter in this scenario added is the levelFilter. Any ideas what I am missing / doing wrong?
UPDATE: Here's the entire .cshtml
for reference:
<h1>@ViewData["Title"]</h1>
<form asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize" method="get">
<div class="form-actions no-color">
<p>
Find by @nameof(Data.Audit.Log.Level):
<select asp-for="@Model.LevelFilter" asp-items="Html.GetEnumSelectList<Microsoft.Extensions.Logging.LogLevel>()">
<option selected="selected" value="@Model.LevelFilter">Please select</option>
</select>
<input type="submit" value="Search" class="btn btn-primary" asp-route-pageSize="@Model.Logs.PageSize" /> |
<a asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize">Back to full List</a>
</p>
</div>
</form>
<table class="usa-table usa-table--striped usa-table--borderless">
<thead>
<tr>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Logged)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Level)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Message)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].UserName)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Category)
</th>
<th>
Action
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Logs)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Logged)
</td>
<td>
@Html.DisplayFor(modelItem => item.Level)
</td>
<td style="white-space:normal">
@Html.DisplayFor(modelItem => item.Message)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Category)
</td>
<td>
<a asp-page="./Details" asp-route-id="@item.Id" class="usa-button usa-button--outline">Details</a>
</td>
</tr>
}
</tbody>
</table>
<div id="pagingDiv" class="pages-div">
@if (Model.Logs.HasPrevPage)
{
<a asp-page="./Index" asp-route-pageIndex="@(Model.Logs.PageIndex - 1)" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">Prev</a>
}
@if (Model.Logs.ShowFirstPage)
{
<a asp-page="./Index" asp-route-pageIndex="@(1)" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">1</a>
<div class="inline-div">...</div>
}
@for (int i = Model.Logs.MinPage; i <= Model.Logs.MaxPage; i++)
{
if (Model.Logs.PageIndex == i)
{
<div class="non-button usa-button usa-button--active">@i.ToString()</div>
}
else
{
<a asp-page="./Index" asp-route-pageIndex="@i" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">@i.ToString()</a>
}
}
@if (Model.Logs.ShowLastPage)
{
<div class="inline-div">...</div>
<a asp-page="./Index" asp-route-pageIndex="@Model.Logs.TotalPages" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">@Model.Logs.TotalPages</a>
}
@if (Model.Logs.HasNextPage)
{
<a asp-page="./Index" asp-route-pageIndex="@(Model.Logs.PageIndex + 1)" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">Next</a>
}
</div>
<div id="pagingDiv" class="records-per-page-div">
@foreach(var i in Model.Logs.PageSizes)
{
if (Model.Logs.PageSize == i)
{
<div class="non-button usa-button usa-button--active">@i.ToString()</div>
}
else
{
<a asp-page="./Index" asp-route-pageIndex="@Model.Logs.PageIndex" asp-route-pageSize="@i" asp-route-prevPageSize="@Model.Logs.PageSize"
asp-route-levelFilter="@
<details>
<summary>英文:</summary>
I have an `Index` Razor page implemented with paging based on the tutorial [here][1]. I also implemented a user-selectable page sizing (details not shown here for brevity as it is not applicable to this problem) as well which I all have working. I am attempting to add the search functionality from the same page [here][2]. I have the search functionality working as well.
What I am struggling to get working is routing the current page size to the Get method when the search button is clicked.
Here is my search form from the index page:
<form asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize" method="get">
<div class="form-actions no-color">
<p>
Find by @nameof(Data.Audit.Log.Level):
<select asp-for="@Model.LevelFilter" asp-items="Html.GetEnumSelectList<Microsoft.Extensions.Logging.LogLevel>()">
<option selected="selected" value="@Model.LevelFilter">Please select</option>
</select>
<input type="submit" value="Search" class="btn btn-primary" asp-route-pageSize="@Model.Logs.PageSize" /> |
<a asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize">Back to full List</a>
</p>
</div>
</form>
And here is the `Get` method on the `Index` page:
public PaginatedList<Data.Audit.Log> Logs { get; set; }
public LogLevel? LevelFilter { get; set; }
public async Task OnGetAsync(int? pageIndex, int? pageSize, int? prevPageSize, LogLevel? levelFilter)
{
LevelFilter = levelFilter;
if (prevPageSize != null && pageSize != null && pageIndex != null && pageIndex > 1)
{
pageIndex = PaginatedList<Data.Audit.Log>.CalculateNewPageIndex(pageIndex.Value, prevPageSize.Value, pageSize.Value);
}
var query = DbContext.Logs.AsQueryable();
if(levelFilter != null)
{
query = query.Where(x => x.Level == levelFilter.ToString());
}
Logs = await PaginatedList<Data.Audit.Log>.CreateAsync(query.OrderByDescending(x => x.Logged).AsNoTracking(), pageIndex ?? 1, pageSize ?? _defaultPageSize);
}
As you can see in the `.cshtml` markup, I have tried putting the `asp-route-pageSize` on the form, input, and even the 'a' tag in the form. The html seems to be generated correctly:
[![enter image description here][3]][3]
The "Back for full List" behaves correctly and maintains the pageSize, however the Search button loses it. The only query parameter in this scenario added is the levelFilter. Any ideas what I am missing / doing wrong?
**UPDATE**: Here's the entire `.cshtml` for reference:
<h1>@ViewData["Title"]</h1>
<form asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize" method="get">
<div class="form-actions no-color">
<p>
Find by @nameof(Data.Audit.Log.Level):
<select asp-for="@Model.LevelFilter" asp-items="Html.GetEnumSelectList<Microsoft.Extensions.Logging.LogLevel>()">
<option selected="selected" value="@Model.LevelFilter">Please select</option>
</select>
<input type="submit" value="Search" class="btn btn-primary" asp-route-pageSize="@Model.Logs.PageSize" /> |
<a asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize">Back to full List</a>
</p>
</div>
</form>
<table class="usa-table usa-table--striped usa-table--borderless">
<thead>
<tr>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Logged)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Level)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Message)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].UserName)
</th>
<th scope="col">
@Html.DisplayNameFor(model => model.Logs[0].Category)
</th>
<th>
Action
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Logs)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Logged)
</td>
<td>
@Html.DisplayFor(modelItem => item.Level)
</td>
<td style="white-space:normal">
@Html.DisplayFor(modelItem => item.Message)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Category)
</td>
<td>
<a asp-page="./Details" asp-route-id="@item.Id" class="usa-button usa-button--outline">Details</a>
</td>
</tr>
}
</tbody>
</table>
<div id="pagingDiv" class="pages-div">
@if (Model.Logs.HasPrevPage)
{
<a asp-page="./Index" asp-route-pageIndex="@(Model.Logs.PageIndex - 1)" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">Prev</a>
}
@if (Model.Logs.ShowFirstPage)
{
<a asp-page="./Index" asp-route-pageIndex="@(1)" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">1</a>
<div class="inline-div">...</div>
}
@for (int i = Model.Logs.MinPage; i <= Model.Logs.MaxPage; i++)
{
if (Model.Logs.PageIndex == i)
{
<div class="non-button usa-button usa-button--active">@i.ToString()</div>
}
else
{
<a asp-page="./Index" asp-route-pageIndex="@i" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">@i.ToString()</a>
}
}
@if (Model.Logs.ShowLastPage)
{
<div class="inline-div">...</div>
<a asp-page="./Index" asp-route-pageIndex="@Model.Logs.TotalPages" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">@Model.Logs.TotalPages</a>
}
@if (Model.Logs.HasNextPage)
{
<a asp-page="./Index" asp-route-pageIndex="@(Model.Logs.PageIndex + 1)" asp-route-pageSize="@Model.Logs.PageSize" asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">Next</a>
}
</div>
<div id="pagingDiv" class="records-per-page-div">
@foreach(var i in Model.Logs.PageSizes)
{
if (Model.Logs.PageSize == i)
{
<div class="non-button usa-button usa-button--active">@i.ToString()</div>
}
else
{
<a asp-page="./Index" asp-route-pageIndex="@Model.Logs.PageIndex" asp-route-pageSize="@i" asp-route-prevPageSize="@Model.Logs.PageSize"
asp-route-levelFilter="@Model.LevelFilter" class="usa-button usa-button--outline">@i.ToString()</a>
}
}
</div>
[1]: https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-7.0#add-paging
[2]: https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-7.0#add-filtering
[3]: https://i.stack.imgur.com/0KTJo.png
</details>
# 答案1
**得分**: 2
在[@pcalkins][1]的大力帮助下,这里是正确的答案。我不得不添加隐藏的输入标签,映射到Model.Logs.PageSize,并给它命名为我想要的查询字符串参数:
```html
<input asp-for="@Model.Logs.PageSize" name="pageSize" type="hidden" />
这是完整的表单:
<form asp-page="./Index" method="get">
<div class="form-actions no-color">
<input asp-for="@Model.Logs.PageSize" name="pageSize" type="hidden" />
<p>
按@nameof(Data.Audit.Log.Level)查找:
<select asp-for="@Model.LevelFilter" asp-items="Html.GetEnumSelectList<Microsoft.Extensions.Logging.LogLevel>()">
<option selected="selected" value="@Model.LevelFilter">请选择</option>
</select>
<input type="submit" value="搜索" class="btn btn-primary" /> |
<a asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize">返回到完整列表</a>
</p>
</div>
</form>
英文:
With much help from @pcalkins, here is the correct answer. I had to add the hidden input tag mapped to the Model.Logs.PagSize as well as give it the name of the parameter I wanted on the query string:
<input asp-for="@Model.Logs.PageSize" name="pageSize" type="hidden" />
Here's the full form:
<form asp-page="./Index" method="get">
<div class="form-actions no-color">
<input asp-for="@Model.Logs.PageSize" name="pageSize" type="hidden" />
<p>
Find by @nameof(Data.Audit.Log.Level):
<select asp-for="@Model.LevelFilter" asp-items="Html.GetEnumSelectList<Microsoft.Extensions.Logging.LogLevel>()">
<option selected="selected" value="@Model.LevelFilter">Please select</option>
</select>
<input type="submit" value="Search" class="btn btn-primary" /> |
<a asp-page="./Index" asp-route-pageSize="@Model.Logs.PageSize">Back to full List</a>
</p>
</div>
</form>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论