0

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.

18
  • could you clarify the purpose of setting values to tempdata in the viewstart? Commented Jun 19, 2023 at 13:21
  • @AdalyatNazirov to make them global and accessible in all views Commented Jun 19, 2023 at 13:40
  • TempData is already global, you don't need to "initialise" it in viewstart explicitly. Moreover, in your case you override the value came from controller Commented Jun 19, 2023 at 13:50
  • @AdalyatNazirov thanks! I removed it from viewStart. But still I have the null value problem when I navigate to another view. Commented Jun 19, 2023 at 13:55
  • cool! Can you now edit the question and put more info about your navigation? The code currently in question perfectly works (just tested locally). So there must bug in different places or you use complex navigation and TempData items are eliminated. Commented Jun 19, 2023 at 14:14

1 Answer 1

2

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
Sign up to request clarification or add additional context in comments.

8 Comments

I have a problem with keeping the list. It is null when navigating to another view. I mean everytime I click on another's view button, the dropdown list needs to be repopulated. I tried session to keep the list but it doesn't store a list.
I have tried ViewModels. It doesn't work for me. The list is in a partial view and when I add a VM to the partial view, it causes a conflict with the main view's model.
Session or any kind of long-live cache should be okay if you need to persist data between requests for a long time. ViewModel, TempData, and ViewBag are ways to transmit data from one layer to another - it's not about persisting data.
So, for example, you can store data in session, and before render copy them from Session to TemData/ViewBag/etc and then reference it in your view.
If you don't have more questions, please mark answer as accepted (by clicking tick next to answer)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.