Search code examples
c#asp.net-mvcasp.net-coreasp.net-core-mvc

passing parameters through all pages using TempData in asp.net core6 mvc


I have a school database, where there are entities such as Year, Grade, Major, ... I'm trying to create a list of years in the _Layout.cshtml and when the user chooses a year from the list, all views must retrieve data based on the selected year. So I need to first create the list and then pass the selected year to all views. I tried using a viewModel but it didn't work. Now I'm trying to make use of TempData in a way to keep the list of years and the selected year, but I keep receiving the following error.

System.NullReferenceException: Object reference not set to an instance of an object.

Here is my AdminController:

public async Task<IActionResult> Index()
{
    var allYears = await _context.Years.ToListAsync();
    List<SelectListItem> yearList = new List<SelectListItem>();
    foreach (var item in allYears)
    {
        yearList.Add(new SelectListItem { Text = item.Name, Value = item.Id.ToString() });
    }
    TempData["YearList"] = yearList;
    TempData["CurrentYear"] = allYears.FirstOrDefault().Id.ToString();
    return View(TempData);
}

This is the list in the partial view in _Layout.cshtml. This is where I get the error. TempData is null.:

<select class="form-control" asp-for="@TempData["CurrentYear"]">
    @foreach (var item in (List<SelectListItem>)TempData["YearList"])
    {
        <option>@item.Text</option> 
    }    
</select>

I have also added this to _viewStart.cshtml.

TempData["CurrentYear"] = 1;
TempData["YearList"] = null;

I have also tried this, but the issue still remains. Is there any way I can achieve this using TempData? Or Is there a better way?

Edit1: I removed the initialization in viewStart.cshtml. But still I get the null exception error when navigating to another view.

Edit2: I used Session instead of TempData. Also I moved the dropdownlist to a separate view because I had a problem with adding a list to the session. But then RahulSharma sent a tutorial in the comment section which is very helpful. Thank you everyone.


Solution

  • Here is the prototype for your scenario

    Controller:

    [HttpGet]
    public async Task<IActionResult> Index()
    {
        TempData["YearList"] = new List<SelectListItem> { new ("2021", "2021"), new ("2022", "2022"), new ("2023", "2023") };
        return View();
    }
    
    [HttpPost]
    public async Task<IActionResult> Index(int year)
    {
        TempData["CurrentYear"] = year;
        return RedirectToAction(nameof(Index));
    }
    

    View:

    <form method="post">
        <select name="year">
            @foreach (var year in (List<SelectListItem>)TempData["YearList"])
            {
                <option value="@year.Value">@year.Text</option>
            }
        </select>
        <input type="submit" value="Send"/>
    </form>
    
    <h3>Selected year: @TempData["CurrentYear"]</h3>
    

    Explanation:

    1. [HttpGet]Index action method renders years, no selected year on this step
    2. View retrieves the values from TempData and display select control
    3. User choose the year, and click button (submit form)
    4. [HttpPost]Index accept year argument (name matches name attribute of select control). Put the selection into TempData
    5. Redirect to [HttpGet]Index
    6. Renders both list and selected value. The key point here is that if you just refresh the page it won't recover selected value again. Extraction from TempData is one-time operation (read answer https://stackoverflow.com/a/32571656/2091519)

    Points to consider:

    1. pass the selected year via redirect & query string parameters rather than submitting the form (https://stackoverflow.com/a/10175571/2091519)
    2. Use Ajax (async) approach to fetch additional data after user selection (https://stackoverflow.com/a/29122117/2091519)
    3. Use ViewBag/ModelViews or any other container for passing data from contoller to view
    4. Use Cookies/Session to persist data between page reloads etc