用于搜索表单的 ASP.NET MVC 分页

发布于 2024-08-20 07:28:25 字数 564 浏览 6 评论 0原文

我读过几篇关于 MVC 中分页的文章,但没有一篇文章描述了这样的场景:我有一个类似搜索表单的内容,然后希望在用户单击“提交”后在表单下方显示搜索条件(带分页)的结果。

我的问题是,我正在使用的分页解决方案将创建 将传递所需页面的链接,如下所示: http://mysite.com/search/2/ 虽然这一切都很好,但我没有将查询结果发送到内存中的数据库或任何其他内容,因此我需要再次查询数据库。

如果结果由 /Search 的 POST 控制器操作处理,并且数据的第一页也如此呈现,当用户单击移动到时,如何获得相同的结果(基于用户指定的表单条件)第 2 页?

一些 JavaScript 巫术?利用会话状态?使我的 GET 控制器操作具有搜索条件所需的相同变量(但可选),当调用 GET 操作时,实例化一个 FormCollection 实例,填充它并将其传递给 POST 操作方法(从而满足 DRY)?

有人可以为我指出这种情况的正确方向或提供过去已实施的示例吗?谢谢!

I've read several different posts on paging w/ in MVC but none describe a scenario where I have something like a search form and then want to display the results of the search criteria (with paging) beneath the form once the user clicks submit.

My problem is that, the paging solution I'm using will create <a href="..."> links that will pass the desired page like so: http://mysite.com/search/2/ and while that's all fine and dandy, I don't have the results of the query being sent to the db in memory or anything so I need to query the DB again.

If the results are handled by the POST controller action for /Search and the first page of the data is rendered as such, how do I get the same results (based on the form criteria specified by the user) when the user clicks to move to page 2?

Some javascript voodoo? Leverage Session State? Make my GET controller action have the same variables expected by the search criteria (but optional), when the GET action is called, instantiate a FormCollection instance, populate it and pass it to the POST action method (there-by satisfying DRY)?

Can someone point me in the right direction for this scenario or provide examples that have been implemented in the past? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

空气里的味道 2024-08-27 07:28:26

我的方法是使用一个 Action 来处理 post 和 get 场景。

这是我的,可以通过 GET 和 POST 方法处理:

public ViewResult Index([DefaultValue(1)] int page,
                        [DefaultValue(30)] int pageSize,
                        string search,
                        [DefaultValue(0)] int regionId,
                        [DefaultValue(0)] int eventTypeId,
                        DateTime? from,
                        DateTime? to)
{
    var events = EventRepo.GetFilteredEvents(page, pageSize, search, regionId, eventTypeId, from, to);
    var eventFilterForm = EventService.GetEventFilterForm(from, to);

    var eventIndexModel = new EventIndexModel(events, eventFilterForm);

    return View("Index", eventIndexModel);
}

eventFilterForm 是一个表示模型,其中包含我的搜索表单的一些 IEnumerable 属性。

eventIndexModel 是一个表示模型,它结合了 eventFilterForm 和搜索结果 - events

events 是一个IPgedList 的特殊类型。您可以在此处 和此处。第一个链接讨论 IPgedList,而第二个链接有您应该需要的高级分页方案。

高级分页具有我使用的以下方法:

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, RouteValueDictionary valuesDictionary)

我像这样使用它:

<%= Html.Pager(Model.Events.PageSize,
               Model.Events.PageNumber,
               Model.Events.TotalItemCount,
               new
               {
                   action = "index",
                   controller = "search",
                   search = ViewData.EvalWithModelState("Search"),
                   regionId = ViewData.EvalWithModelState("RegionId"),
                   eventTypeId = ViewData.EvalWithModelState("EventTypeId"),
                   from = ViewData.EvalDateWithModelState("From"),
                   to = ViewData.EvalDateWithModelState("To")
               }) %>

这将创建如下所示的链接:

/event/search?regionId=4&eventTypeId=39&from=2009/09/01&to=2010/08/31&page=3

HTH,
查尔斯

·诗篇。 EvalWithModelState 如下:

PP。如果您要将日期放入 get 变量中 - 我建议阅读 我的博客文章...:-)

/// <summary>
/// Will get the specified key from ViewData. It will first look in ModelState
/// and if it's not found in there, it'll call ViewData.Eval(string key)
/// </summary>
/// <param name="viewData">ViewDataDictionary object</param>
/// <param name="key">Key to search the dictionary</param>
/// <returns>Value in ModelState if it finds one or calls ViewData.Eval()</returns>
public static string EvalWithModelState(this ViewDataDictionary viewData, string key)
{
    if (viewData.ModelState.ContainsKey(key))
        return viewData.ModelState[key].Value.AttemptedValue;

    return (viewData.Eval(key) != null) ? viewData.Eval(key).ToString() : string.Empty;
}

My method is to have an Action that handles both the post and the get scenarios.

This is my which can be handled by both GET and POST methods:

public ViewResult Index([DefaultValue(1)] int page,
                        [DefaultValue(30)] int pageSize,
                        string search,
                        [DefaultValue(0)] int regionId,
                        [DefaultValue(0)] int eventTypeId,
                        DateTime? from,
                        DateTime? to)
{
    var events = EventRepo.GetFilteredEvents(page, pageSize, search, regionId, eventTypeId, from, to);
    var eventFilterForm = EventService.GetEventFilterForm(from, to);

    var eventIndexModel = new EventIndexModel(events, eventFilterForm);

    return View("Index", eventIndexModel);
}

The eventFilterForm is a presentation model that contains some IEnumerable<SelectListItem> properties for my search form.

The eventIndexModel is a presentation model that combines the eventFilterForm and the results of the search - events

The events is a special type of IPagedList. You can get more information and code for that here and here. The first link talks about IPagedList where as the second link has an Advanced Paging scenario which you should need.

The advanced paging has the following method that I use:

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, RouteValueDictionary valuesDictionary)

And I use it like so:

<%= Html.Pager(Model.Events.PageSize,
               Model.Events.PageNumber,
               Model.Events.TotalItemCount,
               new
               {
                   action = "index",
                   controller = "search",
                   search = ViewData.EvalWithModelState("Search"),
                   regionId = ViewData.EvalWithModelState("RegionId"),
                   eventTypeId = ViewData.EvalWithModelState("EventTypeId"),
                   from = ViewData.EvalDateWithModelState("From"),
                   to = ViewData.EvalDateWithModelState("To")
               }) %>

This creates links that look like:

/event/search?regionId=4&eventTypeId=39&from=2009/09/01&to=2010/08/31&page=3

HTHs,
Charles

Ps. EvalWithModelState is below:

PPs. If you are going to put dates into get variables - I would recommend reading my blog post on it... :-)

/// <summary>
/// Will get the specified key from ViewData. It will first look in ModelState
/// and if it's not found in there, it'll call ViewData.Eval(string key)
/// </summary>
/// <param name="viewData">ViewDataDictionary object</param>
/// <param name="key">Key to search the dictionary</param>
/// <returns>Value in ModelState if it finds one or calls ViewData.Eval()</returns>
public static string EvalWithModelState(this ViewDataDictionary viewData, string key)
{
    if (viewData.ModelState.ContainsKey(key))
        return viewData.ModelState[key].Value.AttemptedValue;

    return (viewData.Eval(key) != null) ? viewData.Eval(key).ToString() : string.Empty;
}
苍白女子 2024-08-27 07:28:26

使搜索参数成为视图模型的一部分:

public SearchViewModel
{
    string SearchParameters { get; set; }
    List<SearchObjects> SearchResults { get;set; }
}

然后只需将搜索文本框设置为等于SearchParameters。

您无法“存储”搜索查询,除非您返回所有结果,然后以某种方式将它们存储在页面中。这是极其低效的。网络是无状态的,因此您必须返回数据库并重新查询以获得更多结果。

Make the Search parameter part of your View Model:

public SearchViewModel
{
    string SearchParameters { get; set; }
    List<SearchObjects> SearchResults { get;set; }
}

Then just set the Search Textbox equal to SearchParameters.

You cannot "store" the search query unless you bring back ALL results and then store those in the page somehow. That is horribly inefficient. The web is stateless, so you will have to go back to the database and re-query for more results.

<逆流佳人身旁 2024-08-27 07:28:26

我明白你在说什么;您可以更改表单以使用按钮并每次都将页面发回。或者,您可以将分页 URL 中的所有条件作为查询字符串变量传递。或者您可以使用 JQuery 进行发布(它有一个 $.post 方法,可以通过链接单击或其他单击调用(http://api.jquery.com/jQuery.post/

I understand what you are saying; you could change the form to use buttons and post the page back everytime. Or, you could pass all the criteria in the URL for the paging as querystring variables. Or you could use JQuery to do the post (it has a $.post method that can be invoked from a link click or other click (http://api.jquery.com/jQuery.post/).

HTH.

窗影残 2024-08-27 07:28:26

如果您在查询字符串中包含搜索文本以及当前结果页面而不是发布搜索文本,则此问题就会消失。作为一个额外的好处,您的用户可以为他们的搜索结果添加书签。

为此,您的搜索按钮只需使用搜索框的当前值构建 GET 请求 URL。这可以通过 JavaScript 或使用 GET 作为搜索表单的方法属性来完成,例如

This problem goes away if you include the search text, as well as the current results page, in your querystring instead of POSTing the search text. As an added benefit, your users can then bookmark their search results.

To do this your search button just needs to build the GET request URL using the current value of the search box. This can be done either in javascript or by using GET as your search form's method attribute, e.g. <form method="get" action="/search">.

夜无邪 2024-08-27 07:28:26

我建议缓存您的搜索结果并为其提供一个 ID。然后,对于每个分页链接,您可以引用搜索 ID 作为参数(在每个搜索页面链接上),并在您的操作中,从缓存中提取它,然后对其进行查询。

使用此方法,除了搜索表单的第一个 POST 提交之外,您无需担心任何其他事情。

请参阅我的帖子了解更多详细信息。

I recommend cacheing your search results and giving them an ID. Then for each paging link, you can reference the search ID as a parameter (on each search page link) and in your action, pull it from cache, then query over it.

Using this method, you don't need to worry about anything other than the first POST submit of the search form.

Refer to my post for more details.

满栀 2024-08-27 07:28:26

我也遇到了同样的问题,这就是我所做的

  1. 从 Nuget 下载 PagedList
  2. 更改您的表单以执行 GET 并创建一个与此类似的 ViewModel 类型(如果您像我一样喜欢 AdventureWorks 和模型绑定):

`

using PagedList;
namespace SearchFormResultPagingExample.Models {

  public class SearchViewModel {
    public int? Page { get; set; }
    public string EmailAddress { get; set; }
    public string LastName { get; set; }
    public IPagedList<Contact> SearchResults { get; set; }
    public string SearchButton { get; set; }
  }
}

`

3.使用 ViewModel 作为控制器操作方法的参数

using System.Linq;
using System.Web.Mvc;
using SearchFormResultPagingExample.Models;
using PagedList; //NOTE: use Nuget to reference PagedList

namespace SearchFormResultPagingExample.Controllers {
    public class SearchController : Controller {
        const int RecordsPerPage = 25;

        public ActionResult Index(SearchViewModel model) {
            if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue) {
                var entities = new AdventureWorksEntities();
                var results = entities.Contacts.Where(c => c.LastName.StartsWith(model.LastName) && c.EmailAddress.StartsWith(model.EmailAddress))
                    .OrderBy(o => o.LastName);

                var pageIndex = model.Page ?? 0;
                model.SearchResults = results.ToPagedList(pageIndex, 25);
            }
            return View(model);
        }
    }
}

使用视图中的寻呼机:

@model SearchFormResultPagingExample.Models.SearchViewModel
@using PagedList.Mvc;

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm("Index", "Search", FormMethod.Get)) {
    @Html.ValidationSummary(false)
    <fieldset>
        <legend>Contact Search</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.EmailAddress)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.EmailAddress)
            @Html.ValidationMessageFor(model => model.EmailAddress)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <p>
            <input name="SearchButton" type="submit" value="Search" />
        </p>
    </fieldset>
}

@if (Model.SearchResults != null && Model.SearchResults.Count > 0) {
    foreach (var result in Model.SearchResults) {
            <hr />
            <table width="100%">
                <tr>
                    <td valign="top" width="*">
                        <div style="font-weight: bold; font-size:large;">@result.LastName, @result.FirstName</div>
                        @result.Title<br />
                        @result.Phone<br />
                        @result.EmailAddress
                    </td>
                </tr>
            </table>
    }
        <hr />

        @Html.PagedListPager(Model.SearchResults,
            page => Url.Action("Index", new RouteValueDictionary() {
               { "Page", page }, 
               { "EmailAddress", Model.EmailAddress },
               { "LastName", Model.LastName }
            }),
            PagedListRenderOptions.PageNumbersOnly)
}

MVC 会将查询字符串强制传入和传出 ViewModel 类型参数。非常光滑!

I had this same problem and here's what I did.

  1. Download PagedList from Nuget
  2. Change your form to do a GET and create a ViewModel type similiar to this (if you love AdventureWorks and Model Binding as much as I do):

`

using PagedList;
namespace SearchFormResultPagingExample.Models {

  public class SearchViewModel {
    public int? Page { get; set; }
    public string EmailAddress { get; set; }
    public string LastName { get; set; }
    public IPagedList<Contact> SearchResults { get; set; }
    public string SearchButton { get; set; }
  }
}

`

3.Use the ViewModel as the parameter to your controller's action method

using System.Linq;
using System.Web.Mvc;
using SearchFormResultPagingExample.Models;
using PagedList; //NOTE: use Nuget to reference PagedList

namespace SearchFormResultPagingExample.Controllers {
    public class SearchController : Controller {
        const int RecordsPerPage = 25;

        public ActionResult Index(SearchViewModel model) {
            if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue) {
                var entities = new AdventureWorksEntities();
                var results = entities.Contacts.Where(c => c.LastName.StartsWith(model.LastName) && c.EmailAddress.StartsWith(model.EmailAddress))
                    .OrderBy(o => o.LastName);

                var pageIndex = model.Page ?? 0;
                model.SearchResults = results.ToPagedList(pageIndex, 25);
            }
            return View(model);
        }
    }
}

Use the pager on in your View:

@model SearchFormResultPagingExample.Models.SearchViewModel
@using PagedList.Mvc;

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm("Index", "Search", FormMethod.Get)) {
    @Html.ValidationSummary(false)
    <fieldset>
        <legend>Contact Search</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.EmailAddress)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.EmailAddress)
            @Html.ValidationMessageFor(model => model.EmailAddress)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <p>
            <input name="SearchButton" type="submit" value="Search" />
        </p>
    </fieldset>
}

@if (Model.SearchResults != null && Model.SearchResults.Count > 0) {
    foreach (var result in Model.SearchResults) {
            <hr />
            <table width="100%">
                <tr>
                    <td valign="top" width="*">
                        <div style="font-weight: bold; font-size:large;">@result.LastName, @result.FirstName</div>
                        @result.Title<br />
                        @result.Phone<br />
                        @result.EmailAddress
                    </td>
                </tr>
            </table>
    }
        <hr />

        @Html.PagedListPager(Model.SearchResults,
            page => Url.Action("Index", new RouteValueDictionary() {
               { "Page", page }, 
               { "EmailAddress", Model.EmailAddress },
               { "LastName", Model.LastName }
            }),
            PagedListRenderOptions.PageNumbersOnly)
}

MVC will coerce the querystring to and from your ViewModel type parameter. It's very slick!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文