NullReferenceException on DropDownListFor after [HttpPost] - c#

In my application I'am populating a dropdownlist from database using ADO Entity Framework, after this when i try to submit the form the value of the dropdown list it is giving Null reference exception.
Error Code (in INDEX.ASPX)
<%: Html.DropDownListFor(model => model.race, Model.Races, "--Select--")%> <--error
<%: Html.ValidationMessageFor(model => model.race)%>
CONTROLLER (in NewPersonController)
public ActionResult Index()
{
Person person= new Person();
return View(new PersonFormViewModel(person));
}
[HttpPost]
public ActionResult Index(Person person)
{
if (ModelState.IsValid) //Also not enter the race parameter
{
personRepo.Add(person);
personRepo.Save();
}
return View(); // When return the view I get the error
}
MODEL (in PersonFormViewModel)
public class PersonFormViewModel
{
public Person Person {
get;
private set;
}
public SelectList Races
{
get;
private set;
}
public string race
{
get { return Person.race; }
set { Person.race = value; }
}
public PersonFormViewModel(Person person)
{
Person = person;
RaceRepository repository = new RaceRepository();
IList<Race> racesList= repository.FindRaces().ToList();
IEnumerable<SelectListItem> selectList =
from c in racesList
select new SelectListItem
{
Text = c.race,
Value = c.id.ToString()
};
Races = new SelectList(selectList, "Value", "Text");
}
}
IN VALIDATION MODEL
[MetadataType(typeof(Person_Validation))]
public partial class Person {
}
public class Person_Validation
{
[Required(ErrorMessage = "Required race")]
public string race
{
get;
set;
}
}
Can you help me please? Thank you.

In the POST method you have to give the same type of model to the view.
[HttpPost]
public ActionResult Index(Person person)
{
if (ModelState.IsValid)
{
personRepo.Add(person);
personRepo.Save();
}
return View(new PersonFormViewModel(person));
}

You're not passing the ViewModel in the post action.
[HttpPost]
public ActionResult Index(Person person)
{
if (ModelState.IsValid) //Also not enter the race parameter
{
personRepo.Add(person);
personRepo.Save();
}
return View(new PersonFormViewModel(person));
}
Or maybe
[HttpPost]
public ActionResult Index(Person person)
{
if (ModelState.IsValid) //Also not enter the race parameter
{
personRepo.Add(person);
personRepo.Save();
}
return RedirectToAction("Index");
}

Related

Form-data is not binding to viewmodel with a generic type

I have an application written using C# on the top of ASP.NET Core 5.0.
I have the following view-model
public class TestVM
{
public Name { get; set; }
public MenuViewModel<string> State { get; set;}
public TestVM()
{
State = MenuViewModel<string>();
}
}
Here is a stripped down version of my MenuViewModel
public class MenuViewModel
{
[BindNever]
public IEnumerable<SelectListItem> Items { get; set; }
}
public class MenuViewModel<T> : MenuViewModel
{
public T Value { get; set; }
}
The problem, is when the post request comes in, the viewModel.State.Value is null. When I evaluate Request.Form I do see the key State.Value with the correct value of CA
Here is a stripped down of my action method in the controller.
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Store(TestVM viewModel)
{
if(ModelState.IsValid)
{
// do some
}
return View(viewModel);
}
How can I bind the form data from the request to State.Value property correctly?
Updated I created an editor-template to allow me to render the MenuVieModel. The ~/Views/Shared/EditorTemplates/MenuViewModel.cshtml contains the following code
#model dynamic
#{
if (!(Model is MenuViewModel m))
{
return;
}
dynamic obj = new System.Dynamic.ExpandoObject();
obj.Class = "form-control";
if (Html.ViewData.ModelMetadata.IsRequired)
{
obj.Required = true;
}
}
#Html.DropDownList("Value", m.Options, Html.ViewData.ModelMetadata.Placeholder, obj)
Firsly,you need know that for each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.
Here is a working demo you could follow:
Model:
public class TestVM
{
public string Name { get; set; }
public MenuViewModel<string> State { get; set; }
public TestVM()
{
State =new MenuViewModel<string>();
}
}
public class MenuViewModel
{
[BindNever]
public IEnumerable<SelectListItem> Items { get; set; }
}
public class MenuViewModel<T> : MenuViewModel
{
public T Value { get; set; }
}
View:
#model dynamic
#{
if (!(Model is MenuViewModel m))
{
return;
}
dynamic obj = new System.Dynamic.ExpandoObject();
obj.Class = "form-control";
if (Html.ViewData.ModelMetadata.IsRequired)
{
obj.Required = true;
}
}
<form asp-action="Store">
#*change here,And I do not find Options in your MenuViewModel,So I change it to Items*#
#Html.DropDownList("State.Value", m.Items, Html.ViewData.ModelMetadata.Placeholder, obj)
<input type="submit" value="post" />
</form>
Controller:
public IActionResult Index()
{
var model = new MenuViewModel<string>()
{
Items = new List<SelectListItem>() {
new SelectListItem() { Value = "-1", Text = "--- Select ---" },
new SelectListItem() { Value = "org1", Text = "org1" },
new SelectListItem() { Value = "org2", Text = "org2" },
new SelectListItem() { Value = "org3", Text = "org3" }
}
};
return View(model);
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Store(TestVM viewModel)
{
if (ModelState.IsValid)
{
// do some
}
return View(viewModel);
}
Result:

How can i validate a model without binding it with the action method?

I am updating database values using an action method but I am using ajax call to send updated values into that method so I am not binding model with this method so how can I validate the model for this action method such as I am not binding this with my method?
public ActionResult Update(int id, double? value)
{
if (!ModelState.IsValid)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json("Not valid model");
}
if (ModelState.IsValid) {
var oldTag = db.Tags.Where(x => x.Id == id).FirstOrDefault();
List<UpdatedData> updatedDatas = new List<UpdatedData> {
new UpdatedData
{
Id=id,
OldTagValue=oldTag.TagValue,
NewTagValue=value,
TagName = oldTag.TagName
}
};
obj.updatedDatas = new List<UpdatedData>();
obj.updatedDatas.AddRange(updatedDatas);
return PartialView("_Update_Confirmation", obj);
}
return View("Index");
}
you can change Update method
public class ProfileViewModel
{
[Required]
public int Id { get; set; }
public double? value { get; set; }
}
then
public ActionResult Update(ProfileViewModel viewModel)
{
if (!ModelState.IsValid)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json("Not valid model");
}
//...
}
The sample DataAnnotations model binder will fill model state with validation errors taken from the DataAnnotations attributes on your model.

Passing model from view to controller using Html.RenderAction results in model being null

I have following model:
public class Foo
{
public List<Employee> Employees{ get; set; }
public List<Company> Companies{ get; set; }
public List<Admin> Admins{ get; set; }
}
Then I have my controller actions:
public ActionResult Index()
{
Foo model = GetFooFromSomewhere();
return PartialView("Index", model);
}
public ActionResult Employees(List<Employee> model)
{
return PartialView("Employees", model);
}
public ActionResult Companies(List<Company> model)
{
return PartialView("Companies", model);
}
public ActionResult Admins(List<Admin> model)
{
return PartialView("Admins", model);
}
Then I have my views
Index.cshml:
#model Foo
#if(Model.Employees.Count > 0)
{
#{Html.RenderAction("Employees", "Config", Model.Employees);}
}
#if(Model.Companies.Count > 0)
{
#{Html.RenderAction("Companies", "Config", Model.Companies);}
}
#if(Model.Admins.Count > 0)
{
#{Html.RenderAction("Admins", "Config", Model.Admins);}
}
Employees.cshtml:
#model List<Employee>
//Display model here
Companies.cshtml
#model List<Company>
//Display model here
Admins.cshtml
#model List<Admin>
//Display model here
As you can see, I use Index.cshtml to get a object that contains multiple lists. This is because I need to hide the actions if no items are found in the list/s. However, when I pass them to the controller again using #Html.RenderAction(...), I get null inside the controller action when I am expecting a List. Why?
Try in this way:
Controller:
public ActionResult Admins(List<Admin> m)
{
return PartialView("Admins", m);
}
View:
#{Html.RenderAction("Admins", "Config", new { m = Model.Admins });}
You have to pass the model initialized to the Index view in the controller.
public ActionResult Index()
{
Foo model = GetFooFromSomewhere();
return PartialView("Index", model);
}

Dropdown value gets null value

I use breakpoint debug it my ClientsId always come null and display on my payments index always is the first value of my Dropdownlist
Model:
public class Payments
{
[Key]
public int PaymentsId { get; set; }
public int ClientId { get; set; }
public virtual Client Client { get; set; }
}
ViewModel:
public class PaymentsViewModel
{
[Required(ErrorMessage = "Please select a client")]
[Display(Name = "Client")]
public int SelectedClient { get; set; }
public IEnumerable<SelectListItem> Client { get; set; }
}
GET CONTROLLER:
public ActionResult Create(Payments model)
{
var liste= new PaymentsViewModel
{
Clients = new SelectList(db.ClientList, "ClientId", "ClientName")
};
return View(liste);
}
POST CONTROLLER:
public ActionResult Create([Bind(Include = "....")] PaymentsViewModel model)
{
if (ModelState.IsValid)
{
model.PaymentsCreate();
return RedirectToAction("Index", "Payments");
}
return View(model);
}
CREATE VIEW:
#Html.DropDownListFor(m => m.SelectedClient, Model.Clients, "-Please select-", new { #class = "form-control" })
</div>
</div>
--------------------------------------------UPDATE---------------------------------------------------
EDIT CONTROLLER (GET):
public ActionResult Edit(int? id, PaymentsViewModel model)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Payments payments = db.PaymentsList.Find(id);
if (payments == null)
{
return HttpNotFound();
}
return View();
}
EDIT CONTROLLER (POST)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "PaymentsId,Paymentnumber,PaymentDate,Amount,Discount,Reference,Total")] Payments payments)
{
if (ModelState.IsValid)
{
db.Entry(payments).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(payments);
}
You should add a ClientsId initialization from model.SelectedClient at PaymentsCreate functions like: ClientsId = model.SelectedClient. And then you need to add SelectedClient string to properties enumeration at Create (post) method to Bind(Include.... attribute

Asp.net MVC #RenderPage with Data

I have controller
public class NewsController : Controller
{
private SchoolDbContext db = new SchoolDbContext();
//
// GET: /News/
public ActionResult Index()
{
return View(db.News.ToList());
}
//
// GET: /News/Details/5
public ActionResult Details(int id = 0)
{
News news = db.News.Find(id);
if (news == null)
{
return HttpNotFound();
}
return View(news);
}
//
// GET: /News/Create
public ActionResult Create()
{
return View();
}
//
// POST: /News/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(News news)
{
if (ModelState.IsValid)
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
string path2 = Path.GetRandomFileName();
fileName = path2 + fileName;
var path = Path.Combine(Server.MapPath("~/Uploads/"), fileName);
news.Image = fileName;
file.SaveAs(path);
}
db.News.Add(news);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(news);
}
//
// GET: /News/Edit/5
public ActionResult Edit(int id = 0)
{
News news = db.News.Find(id);
if (news == null)
{
return HttpNotFound();
}
return View(news);
}
//
// POST: /News/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(News news)
{
if (ModelState.IsValid)
{
db.Entry(news).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(news);
}
//
// GET: /News/Delete/5
public ActionResult Delete(int id = 0)
{
News news = db.News.Find(id);
if (news == null)
{
return HttpNotFound();
}
return View(news);
}
//
// POST: /News/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
News news = db.News.Find(id);
db.News.Remove(news);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
I have a Model
public class News
{
[Key]
public int newsID { get; set; }
[Required]
public string newsName { get; set; }
[Required]
public string newsDescription { get; set; }
public string Image { get; set; }
}
and a simple view
<div class="grid">
#foreach (var item in Model)
{
<div class="holder_content">
<section class="group1">
<h3>#Html.DisplayFor(modelItem => item.newsName)</h3>
<p class="desc">#Html.DisplayFor(modelItem => item.newsDescription)</p>
<a class="photo_hover3" href="#"><img src="~/Uploads/#Html.DisplayFor(modelItem => item.Image)" width="240" height="214" alt=""></a>
<div class="forbutton">
#Html.ActionLink("სრულად ", "Details", new { id = item.newsID }, new { #class = "button" })
</div>
#{ if (User.Identity.IsAuthenticated)
{
#Html.ActionLink("Edit ", "Edit", new { id = item.newsID })
#Html.ActionLink("Delete", "Delete", new { id = item.newsID })
}
}
</section>
}
I want to display this data in another page, where I have this code
#RenderPage("~/Views/News/Index.cshtml")
but web page goes on runtime error, with null pointer exception on foreach tag
have you any solution with this error? sorry for my english. Hope you understand
Please use the partial view rendering.
Note main thing you have to mention the namespace in the view page
Like : #model YourApplicationName.Models.exampleClassName
and then render the page as partial view.
#Html.Partial("partialViewName", new exampleClassName())
or other wise pass the model which you have denoted as namespace in the Partialview like below
#Html.Partial("partialViewName", #Modle.exampleClassName)
or
#Html.Partial("partialViewName", #Modle)

Categories

Resources