Потеря всех данных модели представления при использовании выгружаемого списка

Только что закончил добавлять пейджинг в мое представление с помощью плагина Paged List, и я могу вернуть данные для одной страницы. Только. Если вы нажмете «Далее» или любой другой доступный номер страницы, страница будет повторно отправлена, и вы получите пустую форму.

Я просмотрел код и увидел, что объект модели представления, возвращаемый контроллеру, полностью равен нулю. Кто-нибудь знает способ обойти это (т.е. сохранить данные модели представления при переходе на другую страницу списка страниц)?

Просмотреть модель

public class ZipCodeIndex
{
    [DisplayName("Zip Code")]
    public string searchZip { get; set; }
    public IEnumerable<SelectListItem> StateCodes { get; set; }
    [DisplayName("Effective on this date")]
    public string searchDate { get; set; }
    [DisplayName("State")]
    public string searchState { get; set; }
    [DisplayName("Territory")]
    public string searchTerritory { get; set; }
    [DisplayName("New Territory")]
    public string newTerritory { get; set; }
    [DisplayName("New Description")]
    public string newDescription { get; set; }
    [DisplayName("New Effective Date")]
    public string newEffectiveDate { get; set; }

    public IPagedList<ZipCodeTerritory> pagedTerritoryList { get; set; }
    public List<ZipCodeTerritory> zipCodeTerritory { get; set; }

    public ZipCodeIndex() 
    {
        zipCodeTerritory = new List<ZipCodeTerritory>();
        SetStateCodes();
    }

    //Set state code drop down list
    private void SetStateCodes()
    {
        AgentResources db = new AgentResources();
        StateCodes = (from z in db.ZipCodeTerritory
                      select z.StateCode).Select(x => new SelectListItem
                      {
                          Text = x,
                          Value = x
                      }).Distinct().ToList();

        db.Dispose();
    }
}

Контроллер

    public ViewResult Index(int? page, string errorMessage = "", ZipCodeIndex search = null)
    {
        //set Paged List counter variables
        int pageNumber = page ?? 1;
        int pageSize = 300;

        //If TempData conntains information method call came from another controller action
        if (TempData.Count > 0)
        {
            //Instantiate ZipCodeIndex view model object if it exists
            search = (ZipCodeIndex)TempData["ZipCodeIndexData"];

            //Clear out previous search results
            search.zipCodeTerritory.Clear();
        }

        //Proceed with search
        try
        {
            //If search criteria is null page is loading for the first time so send blank view 
            if (String.IsNullOrWhiteSpace(search.searchZip) &&
                String.IsNullOrWhiteSpace(search.searchDate) &&
                String.IsNullOrWhiteSpace(search.searchState))
            {
                if (!string.IsNullOrWhiteSpace(search.searchTerritory))
                {
                    ViewBag.ErrorMessage = "State or Zip Code required for search.";
                }

                //Convert list to IPagedList for pagining on Index
                search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);

                return View(search);
            }

            //Determine if necessary search criteria has been sent
            if (String.IsNullOrWhiteSpace(search.searchZip) && String.IsNullOrWhiteSpace(search.searchState))
            {
                ViewBag.ErrorMessage = "Either State or Zip Code Must be Specified";

                //Convert list to IPagedList for pagining on Index
                search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);

                return View(search);
            }

            DateTime effectiveDate;

            //Convert date string to DateTime type
            if (String.IsNullOrWhiteSpace(search.searchDate))
            {
                effectiveDate = DateTime.MinValue;
            }
            else
            {
                effectiveDate = Convert.ToDateTime(search.searchDate);
            }

            //Conduct search by State Code/Date alone
            if (String.IsNullOrWhiteSpace(search.searchZip))
            {
                if (string.IsNullOrWhiteSpace(search.searchTerritory))
                {
                    search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                               where z.StateCode.Equals(search.searchState) &&
                                                     z.EffectiveDate >= effectiveDate
                                               select z).ToList();
                }
                else
                {
                    search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                               where z.StateCode.Equals(search.searchState) &&
                                                     z.IndDistrnId.Equals(search.searchTerritory) &&
                                                     z.EffectiveDate >= effectiveDate
                                               select z).ToList();
                }

                //Convert list to IPagedList for pagining on Index
                search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);

            }

            return View(search);
    }

Просмотреть

@model Monet.ViewModel.ZipCodeIndex

@{
    ViewBag.Title = "Zip Code Territory Search";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@using PagedList.Mvc;
@using PagedList;

<h2>Zip Code Territory</h2>

    @using (Html.BeginForm("Index", "ZipCodeTerritory", FormMethod.Post))
    {
        <div class="error" id="searchErrors">
            @ViewBag.ErrorMessage            
        </div>    
        <br/>
        <div id="searchBox" class="boxMe">
            <div id="zipBox">
                @Html.Raw("Zip Code")
                @Html.TextAreaFor(model => model.searchZip, new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" }) 
            </div>
            <div id="dateBox">
                @Html.LabelFor(model => model.searchDate)
                @Html.TextBoxFor(model => model.searchDate, new { style="width: 80px;"})
                <div id="terrBox">
                     @Html.LabelFor(model => model.searchTerritory)
                     @Html.TextBoxFor(model => model.searchTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })                   
                </div>
            </div>
            <div id="stateBox">
                @Html.LabelFor(model => model.searchState)
                @Html.DropDownListFor(model => model.searchState, Model.StateCodes, "  ")
                <button type="submit" id="SearchButton">Search</button>
            </div>
        </div>
        <div style="clear: both;"></div>
    }

<br/>
@Html.ActionLink("Create New", "Create")
<br/>
<br/>
<div class="error" id="updateErrors">
    @ViewBag.UpdateAction            
</div>
@if (Model.zipCodeTerritory.Count > 0)
{
    using (Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
    {
        @Html.HiddenFor(model => model.searchZip)
        @Html.HiddenFor(model => model.searchDate)
        @Html.HiddenFor(model => model.searchState)

        <div id="cloneBox">
            @Html.LabelFor(model => model.newTerritory)
            @Html.TextBoxFor(model => model.newTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })
            @Html.LabelFor(model => model.newDescription)
            @Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;", maxLength = 30 })  
            @Html.LabelFor(model => model.newEffectiveDate)     
            @Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })   
        </div>
        <br/>
        <div id="buttonDiv">
            <button type="submit" id="CloneButton" name="button" value="clone">Update Selected Items</button>
            <button type="submit" id="deleteButton" name="button" value="delete">Delete Selected Items</button>            
        </div>
        <div id="pagingDiv">
            @Html.PagedListPager(Model.pagedTerritoryList, page => Url.Action("Index", new { page })) 
        </div>
        <table id="thetable" class="tablesorter" >
            <thead>
                <th>@Html.CheckBox("SelectAll")</th>
                <th>Channel</th>
                <th>Territory</th>
                <th>Description</th>
                <th>State</th>
                <th>Zip</th>
                <th>Effective</th>
                <th>End Date</th>
                <th>Last Update By</th>
                <th>Last Update Date</th>
                <th></th>
            </thead>
            <tbody id="tableBody">
                @for (int i = 0; i < Model.pagedTerritoryList.Count; i++)
                {
                    <tr>
                        <td>
                            @Html.CheckBoxFor(model => model.pagedTerritoryList[i].Update)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].Update)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].ChannelCode)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].ChannelCode)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].IndDistrnId)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].IndDistrnId)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].DrmTerrDesc)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].DrmTerrDesc)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].StateCode)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].StateCode)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].ZipCode)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].ZipCode)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].EffectiveDate)
                            @Html.HiddenFor(model => model.zipCodeTerritory[i].EffectiveDate)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].EndDate)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].EndDate)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].LastUpdateId)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].LastUpdateId)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].LastUpdateDate)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].LastUpdateDate)
                        </td>
                        <td>
                            @Html.ActionLink("Edit", "Edit", new { id = Model.pagedTerritoryList[i].Id })
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].Id)
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    }
}

ИЗМЕНИТЬ

Нашел эту статью и соответствующим образом изменил @Html.PagedListPager, однако безуспешно (все еще передавая объект модели представления null и теперь даже передавая null page параметр...)

    <div id="pagingDiv">
        @Html.PagedListPager(Model.pagedTerritoryList, page => Url.Action("Index", new RouteValueDictionary()
            {
                { "Page", Page},
                { "search", Model }
            }), PagedListRenderOptions.PageNumbersOnly)
    </div>

person NealR    schedule 08.10.2013    source источник


Ответы (2)


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

Вот пример кода передачи данных модели представления из представления в контроллер и сохранения данных.

Допустим, это ваша модель просмотра:

public class SearchViewModel
{
    public int? Page { get; set; }
    public string data1 { get; set; }
    public string data2 { get; set; }
    public string data3 { get; set; }
    public string data4 { get; set; }
    public IPagedList SearchResults { get; set; }
    public string SearchButton { get; set; }
}

И ваш простой метод построения модели представления (без фактического кода поиска, используемого в качестве примера):

public ActionResult Index(SearchViewModel model) {

    //code using and constructing your returned search results

    var pageNumIndex = model.Page ?? 1;
    model.SearchR = results.ToPagedList(pageIndex, 25);
    return View(model);
}

Тогда, на ваш взгляд, @Html.PagedListPager будет выглядеть так:

@Html.PagedListPager(Model.SearchR, Page => Url.Action("Index", new RouteValueDictionary() {
    { "Page", Page },
    { "data1", Model.data1 },
    { "data2", Model.data2 },
    { "data3", Model.data3 },
    { "data4", Model.data4 },
}),
PagedListRenderOptions.PageNumbersOnly)

Этот подход реализует RouteValueDictionary() непосредственно в Html.PagedListPager.

Я изо всех сил пытался найти решение @Html.PagedListPager с использованием подхода модели представления, пока не обнаружил прямой подход RouteValueDictionary().

person Eli Hellmer    schedule 13.12.2014

Ваш измененный код будет отправлять на страницу индекса только параметры: Страница и поиск. Page - это значение страницы, которое вы хотите, но поиск, вероятно, является ToString вашего класса модели.

Я сам столкнулся с этой проблемой и нашел следующее решение: я добавил этот метод в свою модель и

public RouteValueDictionary GenerateRouteValueDictionary(int page)
    {
        Page = page;
        RouteValueDictionary dic = new RouteValueDictionary
        {
            { QueryHelper.Print(() => Page), Page },
            { QueryHelper.Print(() => PreviousSortField), PreviousSortField },
            { QueryHelper.Print(() => SortOrder), SortOrder },
            { QueryHelper.Print(() => SearchString), SearchString }, 
            { QueryHelper.Print(() => IncludeHidden), IncludeHidden },
            { QueryHelper.Print(() => SearchOperator1), SearchOperator1 },
            { QueryHelper.Print(() => SearchField1), SearchField1 },
            { QueryHelper.Print(() => SearchField1Value), SearchField1Value },
            { QueryHelper.Print(() => WhereOperator), WhereOperator },
            { QueryHelper.Print(() => SearchOperator2), SearchOperator2 },
            { QueryHelper.Print(() => SearchField2), SearchField2 },
            { QueryHelper.Print(() => SearchField2Value), SearchField2Value },
            { QueryHelper.Print(() => FilterByDummyLocations), FilterByDummyLocations },
            { QueryHelper.Print(() => FilterByDummyProfile), FilterByDummyProfile },
            { QueryHelper.Print(() => FilterNoDevice), FilterNoDevice }
        };
        return dic;
    }

И из моего cshtml я генерирую постраничный список следующим образом:

@Html.PagedListPager(Model.SearchResults, page => Model.GenerateRouteValueDictionary(page)));

(QueryHelper.Print — это просто метод, который печатает имя свойства, которое я предоставляю... поэтому я эффективно воссоздаю строку запроса для каждой ссылки. Возможно, это не самый лучший способ справиться с этим, но он работает.

Существует альтернативный подход, при котором вы сначала устанавливаете номер страницы в модели, а затем предоставляете саму модель:

@Html.PagedListPager(Model.SearchResults, page => Url.Action(action, Model.SetPage(page)))

И используя этот дополнительный метод модели:

    public SearchPbxUsersModel SetPage(int page)
    {
        Page = page;
        SearchPbxUsersModel myModel = this.MemberwiseClone() as SearchPbxUsersModel;
        myModel.SearchResults = null;
        return myModel;
    }

Вместо клонирования вы также можете обнулить любые значения, которые вам не нравятся (и вы должны даже обнулить определенные значения, например IPagedList pagedTerritoryList).

При любом подходе есть одно предостережение:

Если у вас также есть элемент управления с множественным выбором, вы застрянете в любом случае, и вам придется писать свои собственные ссылки действий вручную (какая боль). Использование первого подхода не будет работать, потому что с множественным выбором у вас есть свойство строки запроса для элемента управления множественным выбором несколько раз (и это не будет работать ни с одним словарем), а со вторым подходом ваш контейнер, содержащий выбранные значения (например, List или IEnumerable) получит ToString(), поэтому вместо того, чтобы видеть выбранные вами значения, ваша модель будет содержать одно значение, которое является tostring контейнера.

person user3566056    schedule 23.04.2014