Asp.net Mvc3 веб-сетки и подкачки - PullRequest
10 голосов
/ 14 июня 2011

Я пытаюсь выучить Asp.net MVC. Я знаю, что это отличается от форм, и мне нужно изменить свой образ мышления, вероятно. Моя проблема связана с сеткой. Когда я добавляю webgrid на свою страницу и нажимаю кнопку поиска с сообщением Post, он отображает таблицу с пейджером и так далее. Но ссылки на пейджер не являются формой публикации, они просто ссылки, и я потерял все данные своей формы.

Контроллер имеет два метода индекса: один для получения, а другой для отправки. Для get я ничего не делаю, я просто создаю новую viewmodel в этом случае Search class и устанавливаю его для просмотра. Для моего метода записи я беру свою модель представления, выполняю поиск и устанавливаю заполненную модель представления для просмотра.

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

Может быть, пример кода может объяснить это лучше.

Вид:

<form action="" method="post">

Esas no : @Html.TextBoxFor(x=>x.Name)
Yil : @Html.TextBoxFor(x=>x.Year)

<input type="submit" value="Search" />

<hr />
@ViewBag.Message
<hr />

@{ var grid = new WebGrid(Model.Results,rowsPerPage:5);}

@grid.GetHtml(tableStyle:"table",htmlAttributes:new {id="tbl"} )

</form>

Вот мой контроллер: поиск происходит в методе Index Post, и у него есть только мой класс viewmodel.

    private ISearchContext _sc;

    public  MyController(ISearchContext sc)
    {
        _dc = dc;
    }

    //
    // GET: /Dava/

    public ActionResult Index()
    {
        var search = new Search();
        ViewBag.Message = "";
        return View(search);
    }

    [HttpPost]
    public ActionResult Index(Search search)
    {

        Search sres = _dc.SearchFromRepository(search);
        ViewBag.Message = String.Format("Count:{0} ",sres.Results.Count);
        return View(sres);
    }

Поиск модели класса похож на:

public class Search
{
    public int Year { get; set; }
    public string Name { get; set; }


    public IList<Item> Results { get; set; }

    public Search()
    {
        Results = new List<Item>();
    }
}

Ответы [ 4 ]

9 голосов
/ 14 июня 2011

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

Итак, начните с добавления свойства Page nullable integer к вашей Search модели представления и соответствующего скрытого поля в форму, которая будет содержать выбранный номер страницы:

@Html.HiddenFor(x => x.Page, new { id = "page" })

Тогда все, что вам нужно, это небольшой фрагмент JavaScript на странице, чтобы подписаться на событие .click ссылок на пейджер:

$(function () {
    $('tfoot a').click(function () {
        // when the user clicks on any of the pager links
        // try to extract the page number from the link and
        // set the value of the hidden field
        var page = this.href.match(/page=([0-9])+/)[1];
        $('#page').val(page);

        // submit the form so that the POST action is invoked
        // passing along the search criteria (Name and Year) along
        // with the page hidden field value to the Index action
        $('form').submit();

        // cancel the default action of the link which is to simply redirect
        // to the Index action using a GET verb.
        return false;
    });
});
5 голосов
/ 12 августа 2011

Вот обходной путь, который не использует JavaScript.

Проблема, на мой взгляд, в том, что ссылки на пейджинг не получают никакой информации о маршруте, которая должна быть сохранена, например поисковый фильтр.ИМО это вопиющий недосмотр!Немного лишних мыслей избавило бы от головной боли!

Этот метод «отбрасывает» встроенный пейджинг WebGrid и использует помощник для генерации ссылок на пейджинг вместе с ценными данными маршрута, которые мы хотим.

После завершения вы просто визуализируете WebGrid как сетку и используете Помощник для создания ссылок на страницы.Одним из преимуществ здесь является то, что вы можете поместить их сверху и снизу, что нам нравится делать.

Я попытался использовать CSS, аналогичный тому, что предоставляется в Pager.css, который NuGet вставляет в ваше решение.Помощник должен быть достаточно полным для некоторых из вас, но он легко расширяется.

Новый Новый Новый Я только что обновил помощник версией Ajax.Я немного неравнодушен к помощникам Razor, поэтому я не мог понять, как изменить его, чтобы использовать общий шаблон;кто-нибудь, пожалуйста?Важная дополнительная деталь - передать AjaxOptions и убедиться, что в качестве глагола используется POST, иначе вы не сможете выбрать правильный метод контроллера.

Помощник (App_Code / LocalHelpers.cshtml)):

@helper DoPager(System.Web.Mvc.HtmlHelper hh, string pageActionName, WebGrid grid, int maxPageLinks, object rvd) {
<div class="pager">
<div class="pageof">Page <b>@(grid.PageIndex + 1)</b> of <b>@grid.PageCount</b></div>
@if (grid.PageCount > 1) {
<ul>
<li>
@{ RouteValueDictionary rvdp1 = new RouteValueDictionary(rvd);
   rvdp1.Add("Page", 1);
}
@hh.ActionLink("<<", pageActionName, rvdp1)
</li>
@{ int start = Math.Max(0, grid.PageIndex - maxPageLinks / 2); }
@for (int ix = 0; ix + start < grid.PageCount; ix++) {
    int pageno = start + ix + 1;
    var css = hh.Raw(pageno - 1 == grid.PageIndex ? " class=\"highlighted\"" : "");
    RouteValueDictionary rvdp = new RouteValueDictionary(rvd);
    rvdp.Add("Page", pageno);
<li@css>
@hh.ActionLink(pageno.ToString(), pageActionName, rvdp)
</li>
    if (ix >= maxPageLinks) { break; }
}
<li>
@{ RouteValueDictionary rvdpX = new RouteValueDictionary(rvd);
   rvdpX.Add("Page", grid.PageCount);
}
@hh.ActionLink(">>", pageActionName, rvdpX)
</li>
</ul>
}
</div>
}
@helper DoAjaxPager(System.Web.Mvc.AjaxHelper aa, System.Web.Mvc.Ajax.AjaxOptions aopts, System.Web.Mvc.HtmlHelper hh, string pageActionName, WebGrid grid, int maxPageLinks, object rvd) {
<div class="pager">
<div class="pageof">Page <b>@(grid.PageIndex + 1)</b> of <b>@grid.PageCount</b></div>
@if (grid.PageCount > 1) {
<ul>
<li>
@{ RouteValueDictionary rvdp1 = new RouteValueDictionary(rvd);
   rvdp1.Add("Page", 1);
}
@aa.ActionLink("<<", pageActionName, rvdp1, aopts)
</li>
@{ int start = Math.Max(0, grid.PageIndex - maxPageLinks / 2); }
@for (int ix = 0; ix + start < grid.PageCount; ix++) {
    int pageno = start + ix + 1;
    var css = hh.Raw(pageno - 1 == grid.PageIndex ? " class=\"highlighted\"" : "");
    RouteValueDictionary rvdp = new RouteValueDictionary(rvd);
    rvdp.Add("Page", pageno);
<li@css>
@aa.ActionLink(pageno.ToString(), pageActionName, rvdp, aopts)
</li>
    if (ix >= maxPageLinks) { break; }
}
<li>
@{ RouteValueDictionary rvdpX = new RouteValueDictionary(rvd);
   rvdpX.Add("Page", grid.PageCount);
}
@aa.ActionLink(">>", pageActionName, rvdpX, aopts)
</li>
</ul>
}
</div>
}

Просмотр:

<center>
@LocalHelpers.DoPager(Html, "Index", grid, 10, new { CurrentFilter = ViewBag.CurrentFilter })
</center>
@grid.Table(
    tableStyle: "centerit",
    columns: grid.Columns(
        grid.Column(format: @<span>@Html.ActionLink("Edit", "Edit", new { id = item.ID }) | @Html.ActionLink("Details", "Details", new { id = item.ID }) | @Html.ActionLink("Delete", "Delete", new { id = item.ID })</span>),
            grid.Column("PartNumber", "Part Number"),
            grid.Column("Description", "Description"),
            grid.Column("Regex", "Regex")
            )
        )
<center>
@LocalHelpers.DoPager(Html, "Index", grid, 10, new { CurrentFilter = ViewBag.CurrentFilter })
</center>

На мой взгляд, я перерабатываю "CurrentFilter", чтобы знать, что фильтровать.Это связано с действием контроллера (не показано).

3 голосов
/ 11 июня 2012

Хорошо. У меня есть более элегантное решение с использованием AJAX и частичных представлений, которое должно решить эту проблему раз и навсегда

Это моя модель:

public class SearchResultModel
{
        public string SearchText{ get; set; }
        public List<YourObject> Results { get; set; }
        public int TotalResults { get; set; }
}

Вид поиска структурирован так:

@model SearchResultModel
@using (Ajax.BeginForm("SearchAction", "SearchController", new AjaxOptions{UpdateTargetId = "data-grid", HttpMethod="Post"}))
{
        @Html.TextBoxFor(m => m.SearchText)
        <input class="myButton" type="submit" value="Search" />
}
<br />
<div id="data-grid">
       @Html.Partial("SearchResults", new SearchResultModel())
</div>

Частичное представление SearchResults:

@model SearchResultModel
@{
    if (Model.Results != null && Model.Results.Count > 0)
    {
            var grid = new WebGrid(canPage: true, rowsPerPage: 10, canSort: true, ajaxUpdateContainerId: "grid");
            grid.Bind(Model.Results, rowCount: Model.TotalResults, autoSortAndPage: false);
            grid.Pager(WebGridPagerModes.All);

            @grid.GetHtml(htmlAttributes: new { id = "grid" },
            columns: grid.Columns(
                grid.Column("YourColumn1"),
                grid.Column("YourColumn2"),
                grid.Column("YourColumn3")
            ),
            tableStyle: "datatable",
                rowStyle: "datatable-normal",
                    alternatingRowStyle: "datatable-alt"
            );
    }
    else
    {
    <span>No Results</span>
    }
}

Наконец, контроллер:

public class SearchController
{
        public ActionResult SearchAction(SearchResultModel model)
        {
            return RedirectToAction("SearchResults", new { id = model.SearchText });
        }

        public ActionResult SearchResults(string id)
        {
            string searchText = id;
            int page = 1;
            if(Request["page"] != null)
                int.TryParse(Request["page"], out page);

            SearchResultModel model = new SearchResultModel();
            //Populate model according to search text and page number
            //........
            //........
            return PartialView(model);
        }
}

Надеюсь, это поможет кому-то сэкономить время и силы!

0 голосов
/ 20 марта 2013

Мой ответ включает в себя поиск по сеансу, и ничего более.Решение хорошо, потому что вы можете адаптировать его к вашей реальной ситуации, и вам не нужны определенные классы или JQuery.

Волшебный трюк происходит внутри вашего Index ActionResult (или вашего ActionResult по умолчанию, который будет отображать страницу сетки вего поведение по умолчанию).

Пример кода:

    [HttpGet]
    public ActionResult Index()//My default action result that will render the grid at its default situation
    {
        SearchViewModel model = new SearchViewModel(); 

        if (Request.IsAjaxRequest()) //First trick is here, this verification will tell you that someone sorted or paged the grid.
        {
            if (Session["SearchViewModel"] != null) //If session is not empty, you will get the last filtred values from it.
                model = (SearchViewModel)Session["SearchViewModel"];
        }
        else // If it is not an AjaxRequest, you have to clear your Session, so new requests to Index with default behavior won't display filtred values.
        {
            Session["SearchViewModel"] = null;
        }

        model.GridResult = ExecuteFilter(model); // OPITIONAL! This code dependes on how is your real world situation. Just remember that you need to return a default behavior grid if the request was not called by the WebGrid, or return filtred results if WebGrid requested.
        return View(model);
    }

Итак, это будет ваш ActionResult по умолчанию.Он проверит, был ли запрос вызван событием подкачки или сортировки WebGrid, чтобы определить, возвращает ли отфильтрованные результаты или нормальное поведение.

Следующим шагом является поиск POST ActionResult:

    [HttpPost]
    public ActionResult Index(SearchViewModel pesquisa) // IMPORTANT!! It is necessary to be the SAME NAME of your GET ActionResult. The reason for that I know, but won't discuss here because it goes out of the question.
    {
        SearchViewModel model = new SearchViewModel();
        model.GridResult = ExecuteFilter(pesquisa); // Execute your filter
        Session["SearchViewModel"] = model; //Save your filter parameters on Session.
        return View("Index", model);
    }

Это оно.Index.cshtml не имеет никакого трюка.Просто SearchForm для индекса ActionResult, передавая мой SearchViewModel в качестве параметра.

Почему это решение работает?

Хорошо, когда вы нажимаете, чтобы отсортировать или страницу, WebGrid выполняет JavaScript, подобный этому:

$('#yourGrid').load('it pass the url used to display your current Page, and some paging or sorting parameters, but those are used by the WebGrid')

Поскольку он выполняет метод .load (), запрос будет GET и попадет в ваш индекс GET ActionResult.Но это AJAX-вызов, поэтому наш магический трюк снова выполнит фильтр с параметрами, которые вы сохранили в сеансе.

Уникальная деталь, о которой я предупреждаю, касается вашего поведения сетки по умолчанию.GET Index ActionResult ДОЛЖЕН КОГДА-ЛИБО ВЕРНУТЬ верный результат сетки, независимо от того, имеет он фильтры или нет в сеансе.

...