Search code examples
asp.net-corerazor

How to persist model filter values on a Razor page after post


I have a pretty basic .NET Core Razor page that contains a partial view with a table. The index page has a collection of text boxes and dropdowns that are used to filter the table. The table and the filtering functionality works as expected.

I also have a modal popup that is used to add a new record to the base table. There is a submit button on the modal for saving the form values. This also works as expected, except that the text boxes and dropdowns for the filtering are reset after postback.

CSHTML filter

<form>
    <select style="width:150px;" asp-for="Division" asp-items="Model.Divisions">
        <option value="">All</option>
    </select>
    <input type="submit" value="Filter" />
    <a class="link-button">Reset</a>
</form>

CSHTML c#

public class IndexModel : PageModel
    {
        public SelectList? Divisions { get; set; }
        public SelectList? OpCos { get; set; }
        public SelectList? Municipalities { get; set; }
        public SelectList? DesignPhases { get; set; }
        public SelectList? Designers { get; set; }

        [BindProperty(SupportsGet = true)]
        public string? Division { get; set; }

        public async Task OnGetAsync()
        {
            IQueryable<string> filterQuery =
                from m in _context.GasProjects
                where !string.IsNullOrEmpty(m.Division)
                orderby m.Division
                select m.Division;
            Divisions = new SelectList(await filterQuery.Distinct().ToListAsync(), Division);
            GasProjects = await QueryProjects();
        }

Modal Post Handler

public async Task<IActionResult> OnPostCreateNewRecordAsync(GasProject gasProject)
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            //save record
            return RedirectToPage("Index");
        }

I have tried some ViewState and javascript ideas but I haven't hit on the correct solution yet.


Solution

  • You should use <input type="hidden" ...> to save the value of Division. And use TempData to implement it.

    <form method="get">
        <select style="width:150px;" asp-for="Division" asp-items="Model.Divisions">
            <option value="">All</option>
        </select>
        <input type="submit" value="Filter" />
        <a href="/YourController/Reset" class="link-button">Reset</a>
    
        @if (!string.IsNullOrEmpty(Model.Division))
        {
            <input type="hidden" asp-for="Division" value="@Model.Division" />
        }
    </form>
    

    You can save SelectedDivision data in OnGetAsync method, and check the value of TempData["SelectedDivision"] in the OnPostCreateNewRecordAsync method or not.

    Like below:

    public async Task OnGetAsync()
    {
        if (!string.IsNullOrEmpty(Request.Query["Division"]))
        {
            Division = Request.Query["Division"].ToString();
            TempData["SelectedDivision"] = Division;
        }
        else if (TempData.ContainsKey("SelectedDivision"))
        {
            Division = TempData["SelectedDivision"].ToString();
        }
    
        // ...
        IQueryable<string> filterQuery = ...;
        Divisions = new SelectList(await filterQuery.Distinct().ToListAsync(), Division);
        GasProjects = await QueryProjects();
    }
    

    And

    public async Task<IActionResult> OnPostCreateNewRecordAsync(GasProject gasProject)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
        
        // Save record
        
        if (TempData.ContainsKey("SelectedDivision"))
        {
            Division = TempData["SelectedDivision"].ToString();
        }
        
        return RedirectToPage("Index");
    }