Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model is null after postback in pager

On my page I have:

  • a 'filter' section - a couple of checkboxes and textboxes,
  • a 'search' button,
  • grid with paging where the results are displayed. The grid is from Telerik (http://demos.telerik.com/aspnet-mvc/grid/index1), but I don't think this matters.

Searching works OK - I can input some text or check a checkbox in the filter section and the appropriate results are displayed.

Paging works OK only if I use it when the page is loaded (that means before I click Search button, in this case the url is '...Home').

But if click search first (in this case the url will become '...Home/Search') and then try to go to another page on the grid then I get an exception in the Search method, because the model.Filter parameter is null (System.NullReferenceException: Object reference not set to an instance of an object.)

I tried to solve the problem in many different ways (with RedirectToAction method, storing the filter to session and use it in Search method,...) but no solution worked in all scenarios. Any ideas?

My simplified code:

HomeController:

public ActionResult Index()
{
    // On page load display all data without filters.
    var filter = new OverviewFilterModel
    {
        Type1 = true,
        Type2 = true,
        WorkingOrder = ""
    };
    ViewBag.Results = GetResults(filter);
    return View(new HomeModel { Filter = filter });
}

public ActionResult Search(HomeModel model)
{
    ViewBag.Results = GetResults(model.Filter);
    return View("Index");
}

public class OverviewFilterModel
{
    public bool Type1 { get; set; }
    public bool Type2 { get; set; }
    public string WorkingOrder { get; set; }
}

public class HomeModel
{
    public OverviewFilterModel Filter { get; set; }
    public IEnumerable<OverviewResultsModel> Results { get; set; }
}

View:

<!-- ... -->
@model HomeModel
<!-- ... -->

@using (Html.BeginForm("Search", "Home", FormMethod.Post, new { @class = "form-inline" }))
{
    <div class="form-group" style="margin-left: 135px;">
        @Html.CheckBoxFor(p => p.Filter.Type1)@Html.LabelFor(p => p.Filter.Type1, new { style = "margin: 0 15px 0 5px;" })
    </div>
    <!-- a bunch of other checkboxes -->
    <br />
    <div class="form-group">
        <label style="width: 130px; text-align: right;">Delovni nalog</label>
        @Html.TextBoxFor(p => p.Filter.WorkingOrder, new { @class = "form-control ecert-filter-small", @autocomplete = "off" })
    </div>
    <!-- a bunch of other textboxes -->
    <button class="k-button" id="button-refresh" style="margin: 10px 0 0 135px;">Refresh</button>
    <hr />
    @(Html.Kendo().Grid<OverviewResultsModel>()
        .BindTo((IEnumerable<OverviewResultsModel>)ViewBag.Results)
        .Name("gridOverview")
        .Events(p => p.Change("overviewOnRowSelect"))
        .Columns(columns =>
        {
            columns.Template(@<text>@Html.ActionLink("WorkingOrder", "Index", "WO", new { dn = @item.WorkingOrder }, new { @class = "selectable-dn" })</text>).Title("");
            columns.Bound(p => p.Type);
            columns.Bound(p => p.WorkingOrder);
            columns.Bound(p => p.Date);
            columns.Bound(p => p.ProductId);
            columns.Bound(p => p.ProductName);
        })
        .Selectable()
        .Pageable(p => p
            .Refresh(true)
            .PageSizes(true)
            .ButtonCount(10)
            .Messages(q =>
            {
                q.Display("{0} - {1} od {2} records");
                q.Empty("No data for selected filter");
                q.ItemsPerPage("Number of records per page");
            })
        )
        .DataSource(p => p.Server().PageSize(20).Model(q => { q.Id(r => r.WorkingOrder); }))
    )
}
like image 287
sventevit Avatar asked Jun 17 '16 09:06

sventevit


2 Answers

I solved this with the help of session objects - one for filter and one for result. Not the most elegant solution but it works.

public ActionResult Index()
{
    // On page load display all data without filters.
    OverviewFilterModel filter;
    if (Session["filter"] == null) {
        var filter = new OverviewFilterModel
        {
            Type1 = true,
            Type2 = true,
            WorkingOrder = ""
        };
    }
    else {
        filter = (OverviewFilterModel)Session["filter"];
    }

    if (Session["results"] == null){
        ViewBag.Results = GetResults(filter);
    }
    else{
        ViewBag.Results = Session["results"];
    }

    return View(new HomeModel { Filter = filter });
}

public ActionResult Search(HomeModel model)
{
    if (model.Filter == null)
    {
        model.Filter = (OverviewFilterModel)Session["filter"];
    }

    ViewBag.Results = GetResults(model.Filter);
    return View("Index");
}

private IEnumerable<OverviewResultModel> GetResults(OverviewFilterModel filter){
    var data = ...

    Session["results"] = data;
    Session["filter"] = filter;

    return data;
}
like image 162
sventevit Avatar answered Oct 10 '22 03:10

sventevit


Probably this way helps by handle null value of HomeModel.Filter in Search method before executing GetResults(model.Filter):

public ActionResult Search(HomeModel model)
{
    if (model.Filter == null)
    {
        model.Filter = new OverviewFilterModel
        {
            Type1 = true,
            Type2 = true,
            WorkingOrder = ""
        };
    }
    ViewBag.Results = GetResults(model.Filter);
    return View("Index", new HomeModel { Filter = filter });
}

I just figured that null value should be handled by an if-condition to prevent NRE, CMIIW.

like image 45
Tetsuya Yamamoto Avatar answered Oct 10 '22 02:10

Tetsuya Yamamoto