ASP.NET MVC Paging для формы поиска - PullRequest
4 голосов
/ 03 февраля 2010

Я прочитал несколько разных постов на пейджинге с MVC, но ни один не описывает сценарий, когда у меня есть что-то вроде формы поиска, а затем я хочу отобразить результаты критериев поиска (с пейджингом) под формой, как только пользовательнажимает на кнопку "Отправить".

Моя проблема в том, что используемое мной решение для подкачки страниц создаст ссылки, которые будут проходить по нужной странице следующим образом: http://mysite.com/search/2/, и в то время как все в порядке,У меня нет результатов запроса, отправляемого в БД в памяти или чем-то еще, поэтому мне нужно запросить БД снова.

Если результаты обрабатываются действием контроллера POST для / Search и первымСтраница данных отображается как таковая, как я могу получить те же результаты (на основе критериев формы, указанных пользователем), когда пользователь щелкает, чтобы перейти на страницу 2?

Некоторые javascript voodoo?Использовать состояние сеанса?Заставить мое действие контроллера GET иметь те же переменные, которые ожидаются критериями поиска (но необязательно), когда вызывается действие GET, создать экземпляр экземпляра FormCollection, заполнить его и передать его методу действия POST (таким образом, удовлетворяя DRY)?

Может ли кто-нибудь указать мне правильное направление для этого сценария или привести примеры, которые были реализованы в прошлом?Спасибо!

Ответы [ 6 ]

0 голосов
/ 31 марта 2011

У меня была такая же проблема, и вот что я сделал .

  1. Скачать PagedList из Nuget
  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 и обратно.Это очень гладко!

0 голосов
/ 04 февраля 2010

Я рекомендую кэшировать результаты поиска и присваивать им идентификатор. Затем для каждой ссылки подкачки вы можете ссылаться на идентификатор поиска в качестве параметра (для каждой ссылки на страницу поиска) и в своем действии извлекать его из кэша, а затем запрашивать его.

Используя этот метод, вам не нужно беспокоиться ни о чем, кроме первой POST отправки формы поиска.

Обратитесь к моему сообщению для более подробной информации.

0 голосов
/ 03 февраля 2010

Мой метод заключается в том, чтобы иметь действие, которое обрабатывает сценарии как 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<SelectListItem> для моей формы поиска.

eventIndexModel - это модель представления, которая объединяет eventFilterForm и результаты поиска - events

events - это особый тип IPagedList. Вы можете получить больше информации и код для этого здесь и здесь . Первая ссылка говорит о IPagedList, где вторая ссылка имеет сценарий расширенного пейджинга, который вам необходим.

В расширенной подкачке используется следующий метод:

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")
               }) %>

Это создает ссылки, которые выглядят так:

/ событие / поиск? RegionId = 4 & EventTypeID = 39 & с = 2009/09/01 и к = 2010/08/31 и страница = 3

HTHS
Charles

Ps. EvalWithModelState ниже:

PPs. Если вы собираетесь помещать даты в переменные 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;
}
0 голосов
/ 03 февраля 2010

Эта проблема исчезнет, ​​если вы включите в строку запроса текст поиска, а также текущую страницу результатов вместо размещения текста поиска. В качестве дополнительного преимущества ваши пользователи могут добавить в закладки свои результаты поиска.

Для этого вашей кнопке поиска просто нужно создать URL запроса GET, используя текущее значение поля поиска. Это можно сделать либо в javascript, либо с помощью GET в качестве атрибута метода вашей формы поиска, например, <form method="get" action="/search">.

0 голосов
/ 03 февраля 2010

Я понимаю, что вы говорите; Вы можете изменить форму, чтобы использовать кнопки и публиковать страницу каждый раз. Или вы можете передать все критерии в URL для подкачки в виде переменных строки запроса. Или вы можете использовать JQuery для создания поста (у него есть метод $ .post, который может быть вызван нажатием ссылки или другого клика (http://api.jquery.com/jQuery.post/).

НТН.

0 голосов
/ 03 февраля 2010

Сделайте параметр поиска частью вашей модели просмотра:

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

Затем просто установите текстовое поле поиска равным SearchParameters.

Вы не можете «сохранить» поисковый запрос, если не вернете ВСЕ результаты, а затем каким-то образом сохраните их на странице. Это ужасно неэффективно. Сеть не имеет состояния, поэтому вам придется вернуться в базу данных и запросить дополнительные результаты.

...