Using a main view and partial view to add fields dynamically to it with ModelViews. The issue is the ModelState.isValid always returns falls because there are 2 fields in the partial view which are failing validation. These fields are not intended to be mandatory so I have made it nullable but still these are getting validated. How can overcome this situation?
Code: View Main :
@model ViewModel.EnquiryVM
@using (Html.BeginForm("Create", "Enquiries", FormMethod.Post))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.EnquiryNumber, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-3">
@Html.EditorFor(model => model.EnquiryNumber, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.EnquiryNumber, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ClientID, "Client", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-3">
@Html.DropDownListFor(u => u.ClientID, (IEnumerable<SelectListItem>)Model.Clients, "--Select--")
@Html.ValidationMessageFor(model => model.ClientID, "", new { @class = "text-danger" })
</div>
</div>
<div id="LineItems">
<div id="editorRowsLineitems">
@foreach (var item in Model.LineItems)
{
@Html.Partial("_CreateEnquiryItem", item)
}
</div>
@Html.ActionLink("Add Items", "CreateLineItem", null, new { id = "addItem", @class = "button" });
</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>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(function () {
$('#addItem').on('click', function () {
$.ajax({
url: '@Url.Action("CreateLineItem")',
cache: false,
success: function (html) {
$("#editorRowsLineitems").append(html);
$('form').data('validator', null);
$.validator.unobtrusive.parse($('form'));
}
});
return false;
});
$('#editorRowsLineitems').on('click', '.deleteRow', function () {
$(this).closest('.editorRow').remove();
});
$('form').data('validator', null);
$.validator.unobtrusive.parse($('form'));
});
</script>
}
Partial View :
@model ViewModels.EnquiryLineItemVM
<div class="editorRow">
@using (Html.BeginCollectionItem("LineItems"))
{
<table class="table">
<tr>
<td>
@Html.EditorFor(model => model.ItemDesc)
</td>
<td>
@Html.EditorFor(model => model.Quantity)
</td>
<td>
@Html.DropDownListFor(model => model.ManufacturerId, Model.ManufacturerList, "--Please Select--")
</td>
<td>
<a href="#" class="deleteRow">Delete</a>
</td>
</tr>
</table>
}
ViewModels :
public class EnquiryVM
{
public int ID { get; set; }
[Required]
public string EnquiryNumber { get; set; }
public int ClientID { get; set; }
public IEnumerable<SelectListItem> Clients { get; set; }
public int ItemID { get; set; }
public List<EnquiryLineItem> LineItems { get; set; }
}
public class EnquiryLineItemVM
{
public int? EnquiryID { get; set; } //nullable but still validation fires
[Required]
public string ItemDesc { get; set; }
public int Quantity { get; set; }
public int? ManufacturerId { get; set; } // this too fires
public IEnumerable<SelectListItem> ManufacturerList { get; set; }
}
Controller Action Method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create( EnquiryVM enquiryVM)
{
var errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0).Select(k => new { propertyName = k, errorMessage = ModelState[k].Errors[0].ErrorMessage });// for error debugging shows Validation errors for EnquiryID and ManufacturerID
var enquiry = new Enquiry();
enquiry.EnquiryNumber = enquiryVM.EnquiryNumber;
enquiry.ClosingDate = enquiryVM.ClosingDate;
enquiry.RFQSentDate = enquiryVM.RFQSentDate;
enquiry.ClientID = enquiryVM.ClientID;
enquiry.DivisionID = enquiryVM.DivisionID;
enquiry.EnquiryLineItems = enquiryVM.LineItems;
if (ModelState.IsValid)
{
db.Enquiries.Add(enquiry);
enquiryVM.ID = enquiry.ID;
foreach (var item in enquiry.EnquiryLineItems)
{
if (enquiryVM.ID != null)
{
item.EnquiryID = (int)enquiryVM.ID;
}
db.EnquiryLineItems.Add(item);
}
db.SaveChanges();
return RedirectToAction("Index");
}
var viewModel = GetAllCategories();
return View(viewModel);
}
Unable to fix this. Thanks for your time.
ManufacturerID
and EnquiryID
is the FK relationship used in Model , but I'm using ViewModels which is nullable for these fields.
The LineItems
propery of EnquiryVM
is typeof List<EnquiryLineItem>
but the model you have shown for the collection is EnquiryLineItemVM
, not EnquiryLineItem
.
I assume EnquiryLineItem
is you data model and based on the ModelState
errors in the image, both EnquiryId
and ManufacturerId
are not marked as nullable in that data model.
Change your EnquiryVM
to use your EnquiryLineItemVM
view model
public class EnquiryVM
{
public int ID { get; set; }
[Required]
public string EnquiryNumber { get; set; }
....
public List<EnquiryLineItemVM> LineItems { get; set; } // change
}
public class EnquiryLineItemVM
{
....
}