ValidationMessageFor not working as expected - asp.net-mvc-4

I am trying to add validations on my View. But I am unable to access the property inside #Html.ValidationMessageFor(?)
My View
#model IEnumerable<Entity.Employee>
<div class="jumbotron">
#using (Html.BeginForm("Create", "Employee", FormMethod.Post))
{
<label>Name</label>
<input id="txtName" type="text" name="EmployeeName" class="btn btn-default" />
#Html.ValidationMessageFor(model => model. //not able to get the Name property
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
Employee Class
[Required(ErrorMessage="Please enter name")]
public string Name { get; set; }
Controller
[HttpPost]
public ActionResult Create(Employee employee, string EmployeeName)
{
if (ModelState.IsValid)
{
employee.Name = EmployeeName;
repository.SaveRole(role);
return RedirectToAction("Index");
}
else
{
return View(employee);
}
}
I am not sure what have I missed or is it because the View is Strongly couple to IEnumerable<Type>

Your view has defined model of type IEnumerable<Entity.Employee>. This stands for multiple employees. You should change your model declaration to
#model Entity.Employee

Related

Asp.net core 6 Html.DropDownListFor System.NullReferenceException

I have two classes "Customer" and "Deals" the customer has a foreign key relationship to the deal. The user should choose from the dropdown list the customer that makes the deal. So I create a ViewModel class that contains a SelectListItem of customers and a deal and update the creat in the deal controller, the error appears when creating the deals:
System.NullReferenceException HResult=0x80004003 Message=Object reference not set to an instance of an object.
-ViewModel:
public class CreateDealVM
{
public Deals deal { get; set; }
public IEnumerable<SelectListItem> selectedCustomer { get; set; }
public int selectedCustomerId { get; set; }
}
}
The DealController methods
public IActionResult Create()
{
Deals deal = new Deals();
List<Customer> customerList = _context.Customers.ToList();
CreateDealVM vm = new CreateDealVM();
vm.selectedCustomer = new SelectList(customerList, "customerId", "customerName");
vm.deal = deal;
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateDealVM vm)
{
if (ModelState.IsValid)
{
try
{
vm.deal.customer =_context.Customers.Find(vm.selectedCustomerId);
_context.Deals.Add(vm.deal);
_context.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return View(vm);
}
return View();
}
Deal Model class:
namespace MyShop.Models
{
[Table("Deals")]
public class Deals
{
[Key]
[Display(Name = "ID")]
public int dealId { get; set; }
[ForeignKey("Customer")]
[Display(Name = "Customer")]
public Customer customer { get; set; }
[Display(Name = "CustomerName")]
public string? parentCustomerName { get; set; }
[Display(Name = "product")]
public DealTypeEnum product { get; set; }
[Display(Name = "Date")]
public DateTime saleDate { get; set; }
[Display(Name = "Quantity")]
public float quantity { get; set; }
[Display(Name = "Price")]
public float price { get; set; }
}
The view.cshtml page :
#model MyShop.ViewModels.CreateDealVM
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Deals</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
#Html.LabelFor(model => model.selectedCustomer, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.selectedCustomerId, Model.selectedCustomer,"--Select--" )
</div>
</div>
<div class="form-group">
<label asp-for="deal.product" class="control-label"></label>
<select asp-for="deal.product" class="form-control"></select>
<span asp-validation-for="deal.product" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="deal.saleDate" class="control-label"></label>
<input asp-for="deal.saleDate" class="form-control" />
<span asp-validation-for="deal.saleDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="deal.quantity" class="control-label"></label>
<input asp-for="deal.quantity" class="form-control" />
<span asp-validation-for="deal.quantity" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="deal.price" class="control-label"></label>
<input asp-for="deal.price" class="form-control" />
<span asp-validation-for="deal.price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
I tried this link ASP.NET MVC DropdownlistFor Object reference not set to an instance of an object? but it did not work.
I suppose you have this problem not when you render the Create.cshtml view the first time, but after you press Create button and the ModelState.IsValid in the public ActionResult Create(CreateDealVM vm) is false.
After that you just render the Create.cshtml view by calling
return View();
This is why the Model is null when you trying to render the Create.cshtml view the second time.
You should use debugger to check this and if this is exactly what's happening replace return View(); line by
return View(vm);
In additional analyze your code in the view, to find out why the data model become not valid when passed from view to controller.

Editing single entry and show multiple in ASP.NET MVC

I am very new to this and I am trying to show multiple entries to show and have a single button for each entry to edit them. I thought it would be really easy but For some reason I am not getting the clicked entity. Is there way of getting modified entity without running javascript?
Any help will be appreciated. I couldn't find any example code that does this. There are many examples that returns all the entries but not the single element.
Here is my entity class Resource:
public class Resource
{
[Required]
public string title { get; set; }
[Required]
public int value { get; set; }
[Key]
[Required]
public int id { get; set; }
}
On the controller side I have:
[HttpGet]
public IActionResult Index()
{
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return View(resource);
}
Here is the View and EditorTemplates
#model learningMVC.Models.Resource[]
#{
ViewData["Title"] = "index";
}
<h1>index</h1>
<fieldset>
#Html.EditorFor(x => x);
</fieldset>
#addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
#model learningMVC.Models.Resource
<div>
First Name:<br />
<form asp-action="Index" asp-controller="Home">
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="Save" class="btn btn-primary" id="btnSubmit_#Model.id" />
</form>
</div>
In your case, you should use particalview instead of editfor, you can see my demo below.
_ResourcesPartical.cshtml:
#model Resource
<div>
<form method="post" asp-action="Index">
First Name:<br />
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="save" />
</form>
</div>
Your Index view:
#model List<Resource>
#foreach(var m in Model)
{
#await Html.PartialAsync("_ResourcesPartical", m)
}
Controller:
[HttpGet]
public IActionResult Index()
{
//here you get your data.below is my fake data.
var resources= new List<Resource>
{
new Resource
{
id = 1,
title = "aa",
value = 3
},
new Resource
{
id = 2,
title = "bb",
value = 4
}
};
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return RedirectToAction("Index");
}
Test result:

Validations with ViewModel - MVC4

I am trying to implement both Create and List actions in a single View. I was being suggested to use ViewModel. I am getting the object reference error. Also some good example on how to achieve this will help.
My Model Class
public class Employee
{
public int ID { get; set; }
[Required(ErrorMessage="Please enter name")]
public string Name { get; set; }
}
My View Model Class
public class EmployeeVM
{
public Employee Employee { get; set; }
public List<Employee> Employees { get; set; }
}
My Controller
[HttpPost]
public ActionResult Create(EmployeeVM emp, string Name)
{
if (ModelState.IsValid) //my modelstate is valid even when the value is empty string; it then gives an Object reference not set error
{
emp.Employee.Name = Name;
repository.SaveRole(emp);
return RedirectToAction("Index");
}
else
{
return View(emp);
}
}
My View
#model ERP.Domain.Entity.EmployeeVM
<body>
<div class="jumbotron">
#using (Html.BeginForm("Create", "MyController", FormMethod.Post))
{
#Html.ValidationSummary(true)
<label>
Name</label>
<input id="txtName" type="text" name="Name" class="btn btn-default" />
#Html.ValidationMessageFor(model => model.Employee.Name)
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
Also I was suggested in StackOverflow to go with ViewModel approach if I want to use both Create and List in the same View? Is it the right approach. Some example may help.

ViewModel attributes always null when posted

I have the following ViewModel
public class EditPatientViewModel
{
public Domain.Entities.Patient patient;
public IEnumerable<Espece> Especes;
public IEnumerable<Client> Clients;
}
the following controller
public ViewResult Edit(int Id_pat)
{
var ViewModel = new EditPatientViewModel();
ViewModel.patient = patientRepo.GetPatientById(Id_pat);
ViewModel.Especes = especeRepo.Especes;
return View(ViewModel);
}
[HttpPost]
public ActionResult Edit(EditPatientViewModel editPatientViewModel)
{
if (ModelState.IsValid)
{
patientRepo.Save(editPatientViewModel.patient);
TempData["message"] = "Sauvé";
return RedirectToAction("Index");
}
else
{
return View(editPatientViewModel);
}
}
and the following view
#model Veto.Models.ViewModels.EditPatientViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit Patient</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-10">
#Html.HiddenFor(m => m.patient.Id_pat)
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(m => m.patient.Nom_pat)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Every time I submit the form the ViewModel posted is not null but attributes are.. I would like to retrieve the attributes to make an update.
Why?
Tx,
Two same problem in one hour :)
Change your ViewModel to this:
public class EditPatientViewModel
{
public Domain.Entities.Patient patient { get; set; }
public IEnumerable<Espece> Especes { get; set; }
public IEnumerable<Client> Clients { get; set; }
}
In complex types, mvc model binder search for properties not for member variables.

How do I tell what's happening when I postback my view?

I'm working with MVC 4 EF Code First and I have an object with an Edit View and it works properly. Then I created a very similar Create View, only when I click on the submit button I don't hit any breakpoints in the Controller. How do I tell what's wrong here? Is there somewhere I can set a breakpoint in either JavaScript or code-behind?
Model
public class ThirdParty : BaseModel
{
public Int32 Id { get; set; }
[MaxLength(2)]
private String CountryCode { get; set; }
[MaxLength(6)]
private Int32 SequenceNumber { get; set; }
public String UIN
{
get
{
if (CountryCode != null && SequenceNumber > 0)
return CountryCode.ToString() + "-" + SequenceNumber.ToString();
else
return null;
}
set
{
CountryCode = value.ToString().Substring(0, 2);
SequenceNumber = Int32.Parse(value.ToString().Substring(3, value.Length-3));
}
}
[MaxLength(250)]
[Required(AllowEmptyStrings = false, ErrorMessage = "{0} is required.")]
public String Name { get; set; }
public virtual ICollection<RelationshipType> RelationshipTypes { get; set; }
// Address
public Int32 AddressId { get; set; }
[ForeignKey("AddressId")]
public virtual Address Address { get; set; }
public bool IsImported { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Attachment> Attachments { get; set; }
// constructor
public ThirdParty()
{
RelationshipTypes = new HashSet<RelationshipType>();
Attachments = new HashSet<Attachment>();
}
}
Controller Methods
public ActionResult Edit(int id)
{
ThirdPartyContext context = new ThirdPartyContext();
ThirdParty model = context.ThirdParties.Find(id);
ViewBag.Countries = context.Countries.ToList<Country>();
return View(model);
}
[HttpPost]
public ActionResult Edit(string button, ThirdParty model, int id)
{
if (button == "cancel")
return RedirectToAction("Index");
if (ModelState.IsValid)
{
ThirdPartyContext context = new ThirdPartyContext();
model.Address.Country = context.Countries.Find(model.Address.CountryId);
context.Entry(model.Address.Country).State = EntityState.Modified;
context.Entry(model.Address).State = EntityState.Modified;
context.Entry(model).State = EntityState.Modified;
context.SaveChanges();
Success("Third Party record updated!");
return RedirectToAction("Index");
}
else
{
ThirdPartyContext context = new ThirdPartyContext();
model.Address = context.Addresses.Find(model.AddressId);
return View("Edit", model);
}
}
public ActionResult Create()
{
ThirdPartyContext context = new ThirdPartyContext();
ViewBag.Countries = context.Countries.ToList<Country>();
return View();
}
[HttpPost]
public ActionResult Create(ThirdParty model)
{
if (ModelState.IsValid)
{
ThirdPartyContext context = new ThirdPartyContext();
List<ThirdParty> models = context.ThirdParties.ToList<ThirdParty>();
model.Id = models.Count() == 0 ? 1 : models.Select(x => x.Id).Max() + 1;
context.ThirdParties.Add(model);
context.SaveChanges();
Success("Your information was saved!");
return RedirectToAction("Index");
}
Error("There were some errors in your form.");
return View(model);
}
[HttpPost]
public ActionResult Create(string button, ThirdParty model)
{
if (button == "cancel")
return RedirectToAction("Index");
if (ModelState.IsValid)
{
ThirdPartyContext context = new ThirdPartyContext();
List<ThirdParty> models = context.ThirdParties.ToList<ThirdParty>();
model.Id = models.Count() == 0 ? 1 : models.Select(x => x.Id).Max() + 1;
context.ThirdParties.Add(model);
context.SaveChanges();
Success("Your information was saved!");
return RedirectToAction("Index");
}
Error("There were some errors in your form.");
return View(model);
}
Edit View
#model Models.ThirdParty
#{
ViewBag.Title = "Edit";
Layout = "~/Views/shared/ContentLayout.cshtml";
}
<div class="row">
<div class="col-lg-12">
<div class="page-header">
<h2>Third Party</h2>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8">
#using (Html.BeginForm("Edit", "ThirdParty", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-group">
#Html.HiddenFor(model => model.Id)
<div class="clearfix visible-xs"></div>
#Html.HiddenFor(model => model.Address.Id)
</div>
<div class="form-group">
#Html.EditorFor(model => model.UIN)
<div class="clearfix visible-xs"></div>
#Html.EditorFor(model => model.Name)
</div>
#Html.HiddenFor(model => model.AddressId)
#Html.Partial("_EditAddress", Model)
#Html.HiddenFor(model => model.CreatedBy)
#Html.HiddenFor(model => model.CreatedOn)
#Html.HiddenFor(model => model.ModifiedBy)
#Html.HiddenFor(model => model.ModifiedOn)
<p>
<button name="button" type="submit" class="btn btn-default" value="cancel">Cancel</button>
<button name="button" type="submit" class="btn btn-primary" value="submit">Submit</button>
</p>
}
</div>
</div>
** Create View **
#model Models.ThirdParty
#{
ViewBag.Title = "Create";
Layout = "~/Views/shared/ContentLayout.cshtml";
}
<div class="row">
<div class="col-lg-12">
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li>Third Parties</li>
<li class="active">Create New Third Party</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="page-header">
<h2>Create Third Party</h2>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-2"> </div>
<div class="col-lg-8">
#using (Html.BeginForm("Create", "ThirdParty", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.EditorFor(model => model.UIN)
#Html.EditorFor(model => model.Name)
#Html.HiddenFor(model => model.AddressId)
#Html.Partial("_EditAddress", Model)
#Html.HiddenFor(model => model.CreatedBy)
#Html.HiddenFor(model => model.CreatedOn)
#Html.HiddenFor(model => model.ModifiedBy)
#Html.HiddenFor(model => model.ModifiedOn)
<p>
<button name="button" type="submit" class="btn btn-default" value="cancel">Cancel</button>
<input name="button" type="submit" class="btn btn-primary" value="submit" title="Submit" />
<input type="submit" value="Create" />
</p>
}
</div>
<div class="col-lg-2"> </div>
</div>
Edit:
I may have left out a critical part. I have these 4 hidden fields on the ThirdParty and Address models that I am planning to set in the controller Create method (if I can ever get in there). Here's the Model:
public class BaseModel
{
[HiddenInput(DisplayValue = false)]
public Int32 CreatedBy { get; set; }
[HiddenInput(DisplayValue = false)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime CreatedOn { get; set; }
[HiddenInput(DisplayValue = false)]
public Int32 ModifiedBy { get; set; }
[HiddenInput(DisplayValue = false)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ModifiedOn { get; set; }
}
I was thinking that maybe my view is invalid and that's why it's not sending it to the controller. Here's how these look in the page markup:
<input data-val="true" data-val-number="The field CreatedBy must be a number." data-val-required="The CreatedBy field is required." id="Address_CreatedBy" name="Address.CreatedBy" type="hidden" value="" />
<input data-val="true" data-val-date="The field CreatedOn must be a date." data-val-required="The CreatedOn field is required." id="Address_CreatedOn" name="Address.CreatedOn" type="hidden" value="" />
<input data-val="true" data-val-number="The field ModifiedBy must be a number." data-val-required="The ModifiedBy field is required." id="Address_ModifiedBy" name="Address.ModifiedBy" type="hidden" value="" />
<input data-val="true" data-val-date="The field ModifiedOn must be a date." data-val-required="The ModifiedOn field is required." id="Address_ModifiedOn" name="Address.ModifiedOn" type="hidden" value="" />
<div class="form-group">
<input data-val="true" data-val-number="The field CreatedBy must be a number." data-val-required="The CreatedBy field is required." id="CreatedBy" name="CreatedBy" type="hidden" value="" />
<input data-val="true" data-val-date="The field CreatedOn must be a date." data-val-required="The CreatedOn field is required." id="CreatedOn" name="CreatedOn" type="hidden" value="" />
<input data-val="true" data-val-number="The field ModifiedBy must be a number." data-val-required="The ModifiedBy field is required." id="ModifiedBy" name="ModifiedBy" type="hidden" value="" />
<input data-val="true" data-val-date="The field ModifiedOn must be a date." data-val-required="The ModifiedOn field is required." id="ModifiedOn" name="ModifiedOn" type="hidden" value="" />
</div>
If your action gets called, setting a breakpoint at the beginning of your action and attaching to the process should do the trick.
Have you made sure that a POST request is sent from the browser when you click submit on your Create page ?
I haven't tried to run your code but note that you have defined two Create actions with the same name to process the POST verb.
This is likely to be ambiguous for the ASP.NET MVC as it cannot figure out what action to call.
Also, you are defining a ReturnUrl route value in the following line:
#using (Html.BeginForm("Create", "ThirdParty", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal" }))
This doesn't seem to be used in your action handling the POST.
Why do you have 2 submit buttons with the values "submit" and "create" ?
You can remove the Create action that takes the extra button parameter and replace the markup:
<button name="button" type="submit" class="btn btn-default" value="cancel">Cancel</button>
<input name="button" type="submit" class="btn btn-primary" value="submit" title="Submit" />
<input type="submit" value="Create" />
with something like:
<a class="btn btn-default" href="#Url.Action("Index", "ThirdParty")">Cancel</a>
<input name="button" type="submit" class="btn btn-primary" value="Create" title="Create" />
I invite you to read more documentation on how ASP.NET MVC Routing works.