Asp.Net MVC 5 Html.DropDownList is not validating - c#

I've been stuck on this problem for a while now and can't seem to find anything online that helps.
I have a Html.DropDownList in an Editor Template. I use that template in my form and don't select anything for the dropdownlist. When I click submit, I expect the form to notify me that the required dropdownlist doesn't have a value selected, but it doesn't. What am I doing wrong?
Here are the View Models:
public class RequestCreateViewModel
{
public int ID { get; set; }
public TesterLocationCreateViewModel TesterLocationCreateViewModel { get; set; }
....
public RequestCreateViewModel()
{
}
}
public class TesterLocationCreateViewModel
{
public int ID { get; set; }
[Required]
[UIHint("OEM")]
public string OEM { get; set; }
[Required]
[DisplayName("City")]
public string LocationCity { get; set; }
public TesterLocationCreateViewModel()
{
}
}
Here's a snippet of the Create.cshtml
#model WdcTesterManager.Models.Request
#{
ViewBag.Title = "Create Request";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Request</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.EditorFor(model => model.TesterLocationCreateViewModel)
</div>
</div>
}
Here's the TesterLocationCreateViewModel.cshtml (Editor Template):
#model WdcTesterManager.Models.TesterLocationCreateViewModel
<div class="col-md-6">
<h4>Tester Location</h4>
<div class="container-fluid">
<div class="form-group">
#Html.LabelFor(model => model.OEM, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.DropDownList("OEM", (SelectList)ViewBag.OemList, "Choose one", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.OEM, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LocationCity, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.LocationCity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LocationCity, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
When I submit the form without filling anything out, I get a validation error for the City but nothing for the OEM. Any ideas?

#StephenMuecke pointed out that something looked wrong with the code, since the code seemed to be referencing the Request model rather than the Request View model.
So, I took a second look at the code and realized that the Create.cshtml was using the Request model rather than the RequestCreateViewModel.
I'm getting the correct validation errors after changing the Create.cshtml to use the RequestCreateViewModel:
#model WdcTesterManager.Models.RequestCreateViewModel

Related

Foreign key passing as NULL. ASP .NET MVC

I am try to work on simple School management system using ASP .NET MVC 5 and SQL Server 2012 with database first approach.
The entity of student is related with an entity class, as every student would be enrolled in a class. SO have a 'Class' type variable and FK to the Classes table as attributes of my student entity.
Create View of Student has a dropdown list that shows the classes a student can be enrolled in. The dropdown is getting populated finely enough, but when the student is created and is viewed at the Index view, its class is NULL. When I checked the FK of created Students in Server Management studio, i found that the FK is being passed as NULL.
Can someone please let me know what I am doing wrong here.
PS. I am new to ASP .NET and I am not using a viewmodel because the tutorial I followed didn't and also because I didn't feel the need of making one.
Here is my Student.cs
using System;
using System.Collections.Generic;
public partial class Student
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Student()
{
this.Attendences = new HashSet<Attendence>();
}
public int St_id { get; set; }
public string St_name { get; set; }
public string St_guardian_name { get; set; }
public string St_guardian_relation { get; set; }
public string St_guardian_contact { get; set; }
public string St_address { get; set; }
public System.DateTime St_dob { get; set; }
public Nullable<int> St_cl_fk_id { get; set; }
public int St_status { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Attendence> Attendences { get; set; }
public virtual Class Class { get; set; }
}
Here is my Create Action in the StudentsController. PopulateClassDropDown is a helper method.
private void PopulateClassDropDownList(object selectedClass = null)
{
var classQuery = from c in db.Classes
where c.Cl_status==1
select c;
ViewBag.classID = new SelectList(classQuery, "Cl_id", "Cl_name", selectedClass);
}
[Authorize]
// GET: Students/Create
public ActionResult Create()
{
PopulateClassDropDownList();
return View();
}
// POST: Students/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "St_id,St_name,St_guardian_name,St_guardian_relation,St_guardian_contact,St_address,St_dob,St_cl_fk_id.St_status")] Student student)
{
try
{
var temp = student.St_cl_fk_id;
if (ModelState.IsValid)
{
student.St_status = 1;
db.Students.Add(student);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (RetryLimitExceededException /* dex*/ )
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
PopulateClassDropDownList(student.St_cl_fk_id);
return View(student);
}
And finally here is the Create View of Student.
#model GMASchoolProject.Models.Student
#{
ViewBag.Title = "Create";
}
<link rel="stylesheet" type="text/css" href="~/Content/Site.css">
<div class="row" style="margin-bottom:5px;">
<div class="col-lg-12">
<h1 class="page-header">Create New Student</h1>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
Fill in Details
</div>
<div class="panel-body">
<div class="row">
<form class="col-lg-6" role="form">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group" style="margin:15px;">
#Html.LabelFor(model => model.St_name, "Student Name", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.St_name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.St_name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px;">
#Html.LabelFor(model => model.St_guardian_name, "Name of Guardian", htmlAttributes: new { #class = "control-label " })
<div>
#Html.EditorFor(model => model.St_guardian_name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.St_guardian_name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px;">
#Html.LabelFor(model => model.St_guardian_relation, "Guardian's Relation", htmlAttributes: new { #class = "control-label" })
<div>
#Html.DropDownListFor(model => model.St_guardian_relation, new[] {
new SelectListItem() {Text="Parents", Value="Parents" },
new SelectListItem() {Text="Other", Value="Other" }
},"Choose an option", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.St_guardian_relation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px;">
#Html.LabelFor(model => model.St_guardian_contact, "Guardian's Contact", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.St_guardian_contact, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.St_guardian_contact, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px;">
#Html.LabelFor(model => model.St_address, "Student's Address", htmlAttributes: new { #class = "control-label" })
<div>
#Html.TextAreaFor(model=>model.St_address, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.St_address, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px;">
#Html.LabelFor(model => model.St_dob, "Student's Date of Birth", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.St_dob, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.St_dob, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px;">
<label class="control-label col-md-2" for="ClassID">Class</label>
<div>
#Html.DropDownList("classID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.St_cl_fk_id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" style="margin:15px,0,15px,0;">
<div class="col-md-offset-5 col-md-2">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index", null, new { #class = "btn btn-danger" })
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Based on this, make these changes:
// classId implies a scalar value, this should be a list
ViewBag.classList = new SelectList(classQuery, "Cl_id", "Cl_name", selectedClass);
// Change your dropdown code:
#Html.DropDownList("St_cl_fk_id", (SelectList)ViewBag.classList, htmlAttributes: new { #class = "form-control" })
// or better yet:
#Html.DropDownListFor(m => m.St_cl_fk_id, (SelectList)ViewBag.classList, new { #class = "form-control" })
// remove this line - it should bind automatically
var temp = student.St_cl_fk_id;

Html.BeginForm post null model

I have created a create page using asp.net scafolding and below is my page to create expenses.
#model ExpenCare.Models.ExpenseCategory
#{
ViewBag.Title = "CreateExpenseCategories";
}
<div class="row">
#Html.Partial("_RightSidePane")
<div class="col-10 col-sm-8 col-xs-8 col-md-10">
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="list-Profile-Content" role="tabpanel" aria-labelledby="list-Profile">
#using (Html.BeginForm("CreateExpenseCategories", "Admin", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ExpenseCategory</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Expense, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Expense, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Expense, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
On create this page post data to CreateExpenseCategories method of Admin controller.
public ActionResult CreateExpenseCategories(ExpenseCategory expense)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:1888/api/");
var responseTask = client.PostAsJsonAsync("ExpenseCategories", expense);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
return RedirectToAction("ExpenseCategories");
}
else
{
ModelState.AddModelError(string.Empty, "Server Error. Please contact administrator.");
}
return View("ExpenseCategories/CreateExpenseCategories", model: expense);
}
The model used to create this page is as below,
namespace ExpenCare.Models
{
public class ExpenseCategory
{
public int Id { get; set; }
[Required]
public string Expense { get; set; }
public string Description { get; set; }
public Company Company { get; set; }
}
}
When I create an expense this always give null exception error and when I debug, the posted data model is null. This must be a small issue for sure, because I have done other create pages in similar way and I did nit encounter any issue. I must be missing a small thing and this might be a stupid question, but I can't find why this gives a null exception error on post.
I think the problem is the usage of Html.EditorFor,
Try this
#Html.TextBoxFor(model => model => model.Expense,new { #class = "form-control" })
or
#Html.EditorFor(model => model.Expense,null,"Expense",new { htmlAttributes = new { #class = "form-control" } } )
Edit problem solved
Property on type ExpenseCategory has the same parameter 'expense'. Change
public ActionResult CreateExpenseCategories(ExpenseCategory expense)
to
public ActionResult CreateExpenseCategories(ExpenseCategory model)
In your model the Id field is not null able Probably you should add an input or hidden input for it to correct model binding:
#Html.HiddenFor(model => model.Id)
Revise:
I test this case and model binding in mvc is working and setting default value for id. Entirely in absence of an input in form model binding in mvc still working. This behavior is sense because all fields by default are optional and we can set required attribute to enforce inputting them.

How to Bind ViewModel's object with the controller [duplicate]

This question already has answers here:
Asp.Net MVC: Why is my view passing NULL models back to my controller?
(2 answers)
Closed 6 years ago.
In my MVC application, I have a model class like below
public class PROJECT
{
[Display(Name = "Project No.")]
public string PROJECT_NO { get; set; }
[Display(Name = "Title")]
[DataType(DataType.MultilineText)]
[Required]
public string TITLE { get; set; }
[Display(Name = "Description")]
[DataType(DataType.MultilineText)]
public string DESCRIPTION { get; set; }
[Display(Name = "Remarks")]
public string REMARKS { get; set; }
}
And I have a ViewModel like this
public class ProjectViewModel
{
public PROJECT Project { get; set; }
public bool IsSelected { get; set; }
public COMPANY Companies { get; set; }
public CLIENT Clients { get; set; }
}
The ViewModel is the one I am creating the controller and views for. I have created the Index, Details, Delete and Create Views as well. Index, Details and Delete Views just work fine but Create controller model binding does not seem to be working. The ProjectViewModel object that comes in as input to the controller Create is null. How do I bind the ViewModel as a parameter to this object?
// POST: /Project/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ProjectViewModel project)
{
if (ModelState.IsValid)
{
db.PROJECTs.Add(project.Project);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CLIENT_ID = new SelectList(db.CLIENTs, "ID", "NAME", project.Project.CLIENT_ID);
ViewBag.COMPANY_ID = new SelectList(db.COMPANies, "ID", "NAME", project.Project.COMPANY_ID);
return View(project);
}
And here is the Create View
#model IMCCBluePrints.Models.ProjectViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>PROJECT</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Project.PROJECT_NO, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.PROJECT_NO)
#Html.ValidationMessageFor(model => model.Project.PROJECT_NO)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.TITLE, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.TITLE)
#Html.ValidationMessageFor(model => model.Project.TITLE)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.DESCRIPTION, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.DESCRIPTION)
#Html.ValidationMessageFor(model => model.Project.DESCRIPTION)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.REMARKS, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.REMARKS)
#Html.ValidationMessageFor(model => model.Project.REMARKS)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.COMPANY_ID, "COMPANY_ID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("COMPANY_ID", String.Empty)
#Html.ValidationMessageFor(model => model.Project.COMPANY_ID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.CLIENT_ID, "CLIENT_ID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CLIENT_ID", String.Empty)
#Html.ValidationMessageFor(model => model.Project.CLIENT_ID)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
In your GET action instantiate your project class and try:
...
ProjectViewModel.project = new PROJECT();
...
return View(ProjectViewModel);
The Issue that when form submit project conflict with type Project as it is complex type, Just rename it to be model for example
public ActionResult Create(ProjectViewModel model)
try this and it will work, i've tried by myself.

Adding another form to a view on button clicked

I have a Create view which contains a form. I want to give the user two options, save which simply saves the form and redirects to Index or add more tickets which will show another identical form in the view. I am unsure where to start with this, should I save the first ticket details then refresh the page? Or is there a way to have a second form hidden until add more tickets is selected then save all forms at once? I know this is quite vague but I've no idea where to start, but any advice would be great
Here is my model;
public class Ticket
{
public int TicketID { get; set; }
[Required]
[ForeignKey("Event")]
//foreign key
public int EventID { get; set; }
[Required]
public string Description { get; set; }
[Required]
public float Price { get; set; }
//navigation property
public virtual Event Event { get; set; }
//navigation property
public ICollection<OrderDetails> OrderDetails { get; set; }
}
Here is my view so far;
#model GeogSocSite.Models.Ticket
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Ticket</h4>
<hr />
#Html.HiddenFor(model => model.EventID)
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Price, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My choice in this situation is using appropriate view model(as #StephenMuecke ) sugested) and using Bootsrtaap modal(here) to show hidden fields.

How to create or render a List of entity for another entity in ASP.NET MVC 5?

How can I render (Scaffold) list of Tags for a Location having this model in my application:
I'm using Entity Framework 6.1.
I want to save a list of Tags against a location and render it back in View and I'm not able to do that!
public class Tag
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
// Navigation
public virtual Location Location { get; set; }
}
public class Location
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int LocationId { get; set; }
public virtual string LocationName { get; set; }
// Navigation
public virtual List<Tag> Tags { get; set; }
}
Right now the view just contains the Location name for input:
View -Create Location
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Location</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.LocationName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LocationName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LocationName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Tags, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Tags, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Tags, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
It renders fine but doesn't save any tag in database:
Here is the problem
When I'm trying to render back the tags in edit view, it shows like this:
Appreciate any help!
What you're looking for is a custom view/edit templates for your "Tags" property. Refer to this: http://buildstarted.com/2010/09/10/overriding-displayfor-and-editorfor-to-create-custom-outputs-for-mvc/

Categories