Trying to get my head around view model but I get a null error when form validation fails.
FYI, error does not happen when the View is originally loaded. Seems like it can't find the required data when the view loads upon form validation fail.
I also think there is an issue on the way I'm trying to save the data which is likely why it kicks in the form validation error.
View model is as below:
using NVFlexo.Models;
namespace NVFlexo.ViewModels;
public class ItemAndItemCategoriesViewModel
{
public Item Item { get; set; }
public IEnumerable<ItemCategory> ItemCategories { get; set; }
}
Create action in controller --> no errors in this and the view loads as expected with all required data
public IActionResult Create()
{
ItemAndItemCategoriesViewModel itemAndItemCategories = new ItemAndItemCategoriesViewModel();
itemAndItemCategories.ItemCategories = _itemCategoriesService.getAllCategories();
return View(itemAndItemCategories);
}
The create method where the error happens in the form
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ItemAndItemCategoriesViewModel viewModel)
{
if (ModelState.IsValid)
{
ItemCategory cat = _itemCategoriesService.getById(viewModel.Item.ItemCategory.Id);
Item newItem = new Item();
newItem.ItemCode = viewModel.Item.ItemCode;
newItem.ItemDescription = viewModel.Item.ItemDescription;
newItem.Cost = viewModel.Item.Cost;
newItem.SalePrice = viewModel.Item.SalePrice;
newItem.Density = viewModel.Item.Density;
newItem.Micrones = viewModel.Item.Micrones;
newItem.ItemCategory = cat;
_db.Items.Add(newItem);
_db.SaveChanges();
TempData["success"] = "Item created successfully";
return RedirectToAction("Index");
}
ItemAndItemCategoriesViewModel itemAndItemCategories = new ItemAndItemCategoriesViewModel();
itemAndItemCategories.ItemCategories = _itemCategoriesService.getAllCategories();
return View(viewModel);
}
The view where something is likely wrong:
@using NVFlexo.ViewModels;
@model ItemAndItemCategoriesViewModel;
@{
ViewBag.Title = "Create New Item";
Layout = "_Layout";
}
<form method="post">
<div class="border p-3 mt-4">
<div class="row pb-2">
<h2 class="text-primary">Create Item</h2>
<hr/>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="mb-3">
<label asp-for="Item.ItemCode"></label>
<input asp-for="Item.ItemCode" class="form-control" />
<span asp-validation-for="Item.ItemCode" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Item.ItemDescription"></label>
<input asp-for="Item.ItemDescription" class="form-control" />
<span asp-validation-for="Item.ItemDescription" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Item.Cost"></label>
<input asp-for="Item.Cost" class="form-control" type="number"/>
<span asp-validation-for="Item.Cost" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Item.SalePrice"></label>
<input asp-for="Item.SalePrice" class="form-control" type="number"/>
<span asp-validation-for="Item.SalePrice" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Item.Density"></label>
<input asp-for="Item.Density" class="form-control" type="number"/>
<span asp-validation-for="Item.Density" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Item.Micrones"></label>
<input asp-for="Item.Micrones" class="form-control" type="number"/>
<span asp-validation-for="Item.Micrones" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Item.ItemCategory"></label>
<select class="form-control" asp-for="Item.ItemCategory">
@foreach (var category in Model.ItemCategories)
{
<option value="@category.Id">@category.CategoryName</option>
}
</select>
<span asp-validation-for="Item.ItemCategory" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary" style="width: 150px">Create Item</button>
<a asp-controller="Item" asp-action="Index" class="btn btn-secondary" style="width: 150px;">Back to List</a>
</div>
</form>
@section Scripts
{
@{
<partial name="_ValidationScriptsPartial"/>
}
}
This is the error I get
NullReferenceException: Object reference not set to an instance of an object.
AspNetCoreGeneratedDocument.Views_Item_Create.b__25_41() in Create.cshtml, line 50
Line 50 with the error is:
@foreach (var category in Model.ItemCategories)
Stack trace first few lines
AspNetCoreGeneratedDocument.Views_Item_Create.b__25_41() in Create.cshtml + @foreach (var category in Model.ItemCategories) Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() AspNetCoreGeneratedDocument.Views_Item_Create.b__25_0() in Create.cshtml +
Item model class:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace NVFlexo.Models;
[Index(nameof(ItemCode), IsUnique = true)]
public class Item
{
[Key]
public int Id { get; set; }
public string ItemCode { get; set; }
public string ItemDescription { get; set; }
public double Cost { get; set; }
public double SalePrice { get; set; }
public double Micrones { get; set; }
public double Density { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
[DisplayName("Item Category")]
public ItemCategory ItemCategory { get; set; }
}
Appreciate help on this. Likely I've either messed up with my model or view model
When form validation fails, you're not repopulating ItemCategories in your viewModel, leading to a NullReferenceException. To fix this, ensure ItemCategories is populated with all categories in your POST Create action method when validation fails, before returning the view. This prevents the exception by ensuring the dropdown in the form is properly populated.
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ItemAndItemCategoriesViewModel viewModel)
{
if (ModelState.IsValid)
{
ItemCategory cat = _itemCategoriesService.getById(viewModel.Item.ItemCategory.Id);
Item newItem = new Item();
newItem.ItemCode = viewModel.Item.ItemCode;
newItem.ItemDescription = viewModel.Item.ItemDescription;
newItem.Cost = viewModel.Item.Cost;
newItem.SalePrice = viewModel.Item.SalePrice;
newItem.Density = viewModel.Item.Density;
newItem.Micrones = viewModel.Item.Micrones;
newItem.ItemCategory = cat;
_db.Items.Add(newItem);
_db.SaveChanges();
TempData["success"] = "Item Created Successfully";
return RedirectToAction("Index");
}
viewModel.ItemCategories = _itemCategoriesService.getAllCategories();
return View(viewModel);
}