Cannot get the Partialview Model in Parent Controller - c#

I am creating a DataEntry Screen which have a three dropdownlist at the top
which is cascaded using ajax . And A partial view is also rendered using Ajax which will show all the items against that selection user made on the dropdowns. The partialview consist of a table with multiple rolls.
I am not able to get the collection of data selected by the user in the partial view (table )in my controller.
My Controller
[HttpGet]
public ActionResult ApproveLaysheet()
{
LaySheetShortageViewModel mdl = new LaySheetShortageViewModel();
ViewBag.AtcID = new SelectList(db.AtcMasters.Where(o => o.IsClosed == "N"), "AtcId", "AtcNum");
return View(mdl);
}
[HttpGet]
public PartialViewResult GetRollView(decimal[] SelectedOurStyle)
{
LaySheetShortageViewModel model = new LaySheetShortageViewModel();
LaysheetRollRepository lyipores = new LaysheetRollRepository();
model.rolldetailcollection= lyipores.getlaysheetRollData(SelectedOurStyle);
return PartialView("LaySheetRollView",model);
}
[HttpPost]
public ActionResult ApproveLaysheet(LaySheetShortageViewModel Model)
{ // not gretting the value of rolldetailcollection here
return View();
}
My View
#model ArtWebApp.Areas.ArtMVC.Models.ViewModel.LaySheetShortageViewModel
<script type="text/javascript">
$(document).ready(function () {
$("#Show").click(function (e, params) {
debugger;
var SelectedOurStyle = new Array();
SelectedOurStyle = $("#LaySheetID").chosen().val();
if (SelectedOurStyle != null)
{
$.ajax({
url: "#Url.Action("GetRollView", "ApproveLaysheet")",
traditional: true,
data: { 'SelectedOurStyle': SelectedOurStyle },
type: "GET",
success: function (fooBarHTML) {
$("#output").html(fooBarHTML);
},
error: function (xhr, status, errorThrown) {
//...
}
});
}
});
});
</script>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SampCutReqMaster</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="row">
<div class="col-md-2">
#Html.Label("Atcc#", new { #class = "control-label col-md-2" });
</div>
<div class="col-md-10">
#Html.DropDownList("AtcID", null, htmlAttributes: new { #class = "chosen-select form-control" })
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-2">
#Html.Label("OurStyle#", new { #class = "control-label col-md-2" });
</div>
<div class="col-md-10">
#Html.DropDownList("OurStyleID", new MultiSelectList(string.Empty, "Value", "Text") , null, htmlAttributes: new { #class = "chosen-select form-control", #multiple = "multiple" } )
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-2">
#Html.Label("LaySheet#", new { #class = "control-label col-md-2" });
</div>
<div class="col-md-10">
#Html.DropDownList("LaySheetID", new MultiSelectList(string.Empty, "Value", "Text"), null, htmlAttributes: new { #class = "chosen-select form-control", #multiple = "multiple" })
</div>
</div>
</div>
<div id='output' class="">
<!-- Partialview Comes here -->
</div>
</div>
}
My Partial View
#using HtmlHelpers.BeginCollectionItem
#model ArtWebApp.Areas.ArtMVC.Models.ViewModel.LaySheetShortageViewModel
<script src="~/JQuery/GridJQuery.js"></script>
<script>
</script>
<div class="container">
<table class="table table-bordered table-striped table-responsive">
<tr>
<th>
#Html.CheckBox("SelectAll")
</th>
<th>
#Html.DisplayNameFor(model => model.approvelaysheetModel.LaySheetDet_PK)
</th>
<th>
#Html.DisplayNameFor(model => model.approvelaysheetModel.LayCutNum)
</th>
<th>
#Html.DisplayNameFor(model => model.approvelaysheetModel.RollNum)
</th>
</tr>
#if (Model != null)
{
for (int i = 0; i < Model.rolldetailcollection.Count; i++)
{
using (Html.BeginCollectionItem("rolldata"))
{
<tr>
<td>
#Html.EditorFor(modelItem => Model.rolldetailcollection[i].IsSelected, new { #onclick = "Check_ClickNew(this)" })
</td>
<td>
#Html.EditorFor(modelItem => Model.rolldetailcollection[i].LaySheetDet_PK)
#Html.HiddenFor(model => Model.rolldetailcollection[i].LaySheetDet_PK, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.DisplayFor(modelItem => Model.rolldetailcollection[i].LayCutNum)
</td>
<td>
#Html.DisplayFor(modelItem => Model.rolldetailcollection[i].RollNum)
</td>
</tr>
}
}
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="Submit" type="submit" value="Submit Fabric request" class="btn btn-default show" />
</div>
</div>
</div>
My viewModals
public class ApprovelaysheetModel
{
public Decimal ApprovelaysheetModelID { get; set; }
[Display(Name ="ID")]
public Decimal LaySheetDet_PK { get; set; }
public Boolean IsSelected { get; set; }
public Decimal LaySheet_PK { get; set; }
}
public class LaySheetShortageViewModel
{
[Key]
[Display(Name = "ID")]
public Decimal ShortageID { get; set; }
public int Location_pk { get; set; }
public int Atcid { get; set; }
public int OurstyleID { get; set; } }
public List<ApprovelaysheetModel> rolldetailcollection { get; set; }
}
Can anyone suggest whats my mistake or any better method for doing this dataentry as Iam new to MVC

Your use of BeginCollectionItem() inside the for loop is prefixing your name attributes so that they no longer relate to your models properties. Instead of generating
<input type="checkbox" name="rolldetailcollection[0].IsSelected" ... />
your now generating
<input type="checkbox" name="rolldata[####].rolldetailcollection[0].IsSelected" ... />
where #### is a Guid.
Remove the BeginCollectionItem() code so that your loop is
for (int i = 0; i < Model.rolldetailcollection.Count; i++)
{
<tr>
<td>
#Html.EditorFor(m => m.rolldetailcollection[i].IsSelected, new { #onclick = "Check_ClickNew(this)" })
</td>
<td>
#Html.EditorFor(m => m.rolldetailcollection[i].LaySheetDet_PK)
// Note there is no point adding html attributes for a hidden input
#Html.HiddenFor(m => m.rolldetailcollection[i].LaySheetDet_PK)
</td>
....
</tr>
}
Note that the BeginCollectionItem() method is used when you want to dynamically add and remove items from a collection in the view in conjunction with javascript/ajax (refer this answer for an example). If you do want to do that, then you need to create a partial view (say) _ApprovelaysheetModel.cshtml for the model
#model ApprovelaysheetModel
// Note the name needs to match the collection property name
#using (Html.BeginCollectionItem("rolldetailcollection"))
{
....
#Html.EditorFor(m => m.LaySheetDet_PK)
....
}
and then in the main view you use a foreach loop
#foreach(var item in Model.rolldetailcollection)
{
#Html.Partial("_ApprovelaysheetModel", item)
}

Related

How to send IEnumerable back from View to Controller

I'm pretty new to MVC and ASP.Net, my controller has 2 create methods for GET and POST. Through the Get method I'm generating a list and sending it into the view. In the view I'm making changes to a specific value in each var in the list and trying to send it back to the controller and the POST method.
When reaching the Post method, the value of the list is null.
The list I'm sending is a list of products and I'm using a view model to create a class with common values to pass through to the view.
To pass through the IEnumerable collection that was edited by the view I tried using BeginCollectionForm, setting the items through ViewBag, make changes to the model (#model IEnumerable<MarsBurgerV1.ViewModel.ItemVM>), but still each time the checkout button is being pressed the list in the Post method is NULL.
After a lot of tries and changes, currently my code looks as the following:
OrderController.cs (relevant parts)
public class OrderController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Create()
{
var meals = (from m in db.meals
select new ItemVM
{
Id = m.Id,
Name = m.Name,
Price = m.Price,
ItemType = "Meal",
ImageURL = m.ImageUrl,
Quantity = 0
}).ToList();
var drinks = (from d in db.drinks
select new ItemVM
{
Id = d.Id,
Name = d.Name,
Price = d.Price,
ItemType = "Drink",
Quantity = 0
}).ToList();
//More Like That....
List<ItemVM> items = new List<ItemVM>();
items.AddRange(meals);
items.AddRange(drinks);//More Like...
return View(items.ToList());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IEnumerable<ItemVM> items = null)
{
//Here items equals to null.
if (ModelState.IsValid)
{
//.....
}
return View();
}
Views/Order/Create.cshtml:
#model IEnumerable<***.ViewModel.ItemVM>
#{
ViewBag.Title = "Create";
var lst = Model.ToList();
ViewBag.List = Model.ToList();
}
<style>
tr td {
vertical-align: middle;
}
</style>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h3> Order:</h3>
<table class="table table-condensed table-hover">
<tr calss="table-header">
<th>
#Html.DisplayName("Preview")
</th>
<th>
#Html.DisplayNameFor(m => m.Name)
</th>
<th>
#Html.DisplayNameFor(m => m.Price)
</th>
<th>
#Html.DisplayName("Quantity")
</th>
<th></th>
<th></th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#if (Model.ElementAt(i).ImageURL != null)
{
<img src="#Url.Content(Model.ElementAt(i).ImageURL)" alt="IMAGES" height="100" width="100" />
}
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Name)
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Price)
</td>
<td>
<a type="button" class="btn btn-danger btn-xs" href="#">
<span class="glyphicon glyphicon-minus"></span>
</a>
#Html.EditorFor(l => lst[i].Quantity, new { htmlattribute = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => lst[i].Quantity, "", new { #class = "text-danger" })
<a type="button" class="btn btn-success btn-xs" id="plus" href="#">
<span class="glyphicon glyphicon-plus"></span>
</a>
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Checkout" class="btn btn-primary"/>
</div>
</div>
}
ViewModel/ItemVM.cs:
namespace ***.ViewModel
{
public class ItemVM
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
[Required]
public string ItemType { get; set; }
public string ImageURL { get; set; }
[Required]
public int Quantity { get; set; }
}
}
In the end I want to use the "Create" (HttpPost method) to create a new order based on the values in the list received from the view.
What is the proper way of doing that and receiving the IEnumerable into the POST method?
Ok, I was finally able to make it work.
I've changed the #model to List Type,
Add the (actionName, Controller, FormMethod) to the HTML Helper Html.BeginForm,
Used Model[i] inside the loop to access variables and
marked all unchanged variables with
#Html.HiddenFor.
Create.cshtml:
#model List<MarsBurgerV1.ViewModel.ItemVM>
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm("Create","Order", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h3> Order:</h3>
<table class="table table-condensed table-hover">
<tr calss="table-header">
<th>
#Html.DisplayName("Preview")
</th>
<th>
#Html.DisplayName("Name")
</th>
<th>
#Html.DisplayName("Price")
</th>
<th>
#Html.DisplayName("Quantity")
</th>
<th></th>
<th></th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
#Html.HiddenFor(m => Model[i].Id)
#Html.HiddenFor(m => Model[i].Type)
<td>
#Html.HiddenFor(m => Model[i].ImageURL)
#if (Model[i].ImageURL != null)
{
<img src="#Url.Content(Model[i].ImageURL)" alt="IMAGES" height="100" width="100" />
}
#Html.ValidationMessageFor(model => Model[i].ImageURL, "", new { #class = "label-control" })
</td>
<td>
#Html.HiddenFor(m => Model[i].Name)
#Html.DisplayFor(m => Model[i].Name)
#Html.ValidationMessageFor(model => Model[i].Name, "", new { #class = "label-control" })
</td>
<td>
#Html.HiddenFor(m => Model[i].Price)
#Html.DisplayFor(m => Model[i].Price, new { #class = "form-control" })
#Html.ValidationMessageFor(model => Model[i].Price, "", new { #class = "label-control" })
</td>
<td>
<a type="button" class="btn btn-danger btn-xs" href="#">
<span class="glyphicon glyphicon-minus"></span>
</a>
#Html.EditorFor(model => Model[i].Quantity, new { htmlattribute = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => Model[i].Quantity, "", new { #class = "text-danger" })
<a type="button" class="btn btn-success btn-xs" id="plus" href="#">
<span class="glyphicon glyphicon-plus"></span>
</a>
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" value="Checkout" class="btn btn-primary"/>
</div>
</div>
}
Thanks Everyone for the help.

Need ASP.NET MVC create view page to change information provided by user to foreign key values

I am working on creating a website that accesses a table in SQL Management Server. The user needs to be able to view, edit, delete, and create new entries within the table using my site. The table that is being used is called DeviceUnderTest and it has these columns: DeviceUnderTest (pk), DeviceUnderTest (nk), Notes (nk), FaultApplication (fk), Firmware (fk), Hardware (fk), Power (fk), Location (fk), PreEventTime(nk), HandleRate (nk). So far, I have created the model that connects to the main DeviceUnderTest table, as well as the other tables that the foreign key columns reference. I have created the controller, as well as an index, details, delete, and create view page. My index view is a table, and I figured out how to replace all the foreign key values with their corresponding tables from the tables they reference. The one thing I can not figure out is the create page. Users will enter the information in the form of the values that the fk columns reference, rather than the foreign key values themselves. I can not figure out how to change the user input back to the correct foreign key value so that the entry can be correctly added to the table noth in the sql server as well as within my site. If anyone has any suggestions, they are much appreciated.
Controller Code:
// GET: Circuit/Create
public ActionResult Create()
{
return View();
}
// POST: Circuit/Create
[HttpPost]
public ActionResult Create(DeviceUnderTest device)
{
try
{
dbModel.DeviceUnderTests.Add(device);
dbModel.SaveChanges();
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Create View Code:
#model Template_Website.Models.DeviceUnderTest
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DeviceUnderTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.pkDeviceUnderTest, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.pkDeviceUnderTest, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.pkDeviceUnderTest, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkDeviceUnderTest, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkDeviceUnderTest, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkDeviceUnderTest, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkNotes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkNotes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkNotes, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FaultApplication.nkFaultApplication, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FaultApplication.nkFaultApplication, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FaultApplication.nkFaultApplication, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Firmware.nkFirmware, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Firmware.nkFirmware, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Firmware.nkFirmware, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Hardware.nkHardware, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Hardware.nkHardware, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Hardware.nkHardware, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.fkPower, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.fkPower, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.fkPower, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Location.nkLocation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Location.nkLocation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Location, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkPreEventTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkPreEventTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkPreEventTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkHandleRating, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkHandleRating, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkHandleRating, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<<button type="submit" class="btn btn-default " value="Create" onclick="return confirm('Are you sure?')">Create</button>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Index View Code:
#model IEnumerable<Template_Website.Models.DeviceUnderTest>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
#Html.DisplayNameFor(model => model.pkDeviceUnderTest)
</th>
<th>
#Html.DisplayNameFor(model => model.nkDeviceUnderTest)
</th>
<th>
#Html.DisplayNameFor(model => model.nkNotes)
</th>
<th>
#Html.DisplayNameFor(model => model.FaultApplication.nkFaultApplication)
</th>
<th>
#Html.DisplayNameFor(model => model.Firmware.nkFirmware)
</th>
<th>
#Html.DisplayNameFor(model => model.Hardware.nkHardware)
</th>
<th>
#Html.DisplayNameFor(model => model.fkPower)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.nkLocation)
</th>
<th>
#Html.DisplayNameFor(model => model.nkPreEventTime)
</th>
<th>
#Html.DisplayNameFor(model => model.nkHandleRating)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.CheckBoxFor(modelItem => item.Selected)
</td>
<td>
<input type="button" value="Edit" class="btn btn-default" onclick="#("window.location.href='" + #Url.Action("Edit", "Circuit", new { id = item.pkDeviceUnderTest } ) + "'");" />
</td>
<td>
<input type="button" value="Details" class="btn btn-default" onclick="#("window.location.href='" + #Url.Action("Details", "Circuit", new { id = item.pkDeviceUnderTest }) + "'");" />
</td>
<td>
<input type="button" value="Delete" class="btn btn-danger" onclick="#("window.location.href='" + #Url.Action("Delete", "Circuit", new { id = item.pkDeviceUnderTest }) + "'");" />
</td>
<td>
#Html.DisplayFor(modelItem => item.pkDeviceUnderTest)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkDeviceUnderTest)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkNotes)
</td>
<td>
#Html.DisplayFor(modelItem => item.FaultApplication.nkFaultApplication)
</td>
<td>
#Html.DisplayFor(modelItem => item.Firmware.nkFirmware)
</td>
<td>
#Html.DisplayFor(modelItem => item.Hardware.nkHardware)
</td>
<td>
#Html.DisplayFor(modelItem => item.fkPower)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.nkLocation)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkPreEventTime)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkHandleRating)
</td>
</tr>
}
</table>
DeviceUnderTest Model Code:
namespace Template_Website.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class DeviceUnderTest
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public DeviceUnderTest()
{
this.TestRecords = new HashSet<TestRecord>();
}
public bool Selected { get; set; }
[Required]
public int pkDeviceUnderTest { get; set; }
[Required]
public string nkDeviceUnderTest { get; set; }
[Required]
public string nkNotes { get; set; }
public Nullable<int> fkFaultApplication { get; set; }
public Nullable<int> fkFirmware { get; set; }
public Nullable<int> fkHardware { get; set; }
public Nullable<int> fkPower { get; set; }
public Nullable<int> fkLocation { get; set; }
public Nullable<int> nkPreEventTime { get; set; }
public Nullable<int> nkHandleRating { get; set; }
public virtual FaultApplication FaultApplication { get; set; }
public virtual Firmware Firmware { get; set; }
public virtual Hardware Hardware { get; set; }
public virtual Location Location { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<TestRecord> TestRecords { get; set; }
}
public class DeviceUnderTestModel
{
public List<DeviceUnderTest> device { get; set; }
}
}
Hardware Model Code:
namespace Template_Website.Models
{
using System;
using System.Collections.Generic;
public partial class Hardware
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Hardware()
{
this.DeviceUnderTests = new HashSet<DeviceUnderTest>();
}
public int pkHardware { get; set; }
public string nkHardware { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<DeviceUnderTest> DeviceUnderTests { get; set; }
}
}
Users will enter the information in the form of the values that the fk columns reference, rather than the foreign key values themselves.
Why do you want to avoid using the foreign key values? I notice you are using EditorFor but can't you simply use DropdownFor or even a JavaScript-powered autocomplete widget? This way, your users would enter the desired text but the values would be mapped in the background.
For an example of how to use DropdownFor, please see the MSDN docs: https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/working-with-the-dropdownlist-box-and-jquery/examining-how-aspnet-mvc-scaffolds-the-dropdownlist-helper
For an example of using an autocomplete, there is a nice answer here which uses jQuery UI: Autocomplete dropdown in MVC5?
Using the first example for DropdownFor and applying it to your code, one of your model properties would continue to look the same:
public Nullable<int> fkFaultApplication { get; set; }
While you also create a ViewBag item to contain your SelectList which maps your texts and values:
ViewBag.FaultApplicationId = new SelectList(db.FaultApplications, "FaultAppId", "Name");
Finally, you could make use of these properties in your view as follows:
#Html.DropDownList("FaultApplicationId", String.Empty)
Note that the second parameter, String.Empty, "is the text to display when no item is selected."
Just like user1477388 says, you can use dropdowns for each item. This way the view displays the text, but the value sent back is the id.
For example using hardware. I'm using a viewmodel so we dont present the database models to the view. The VM has the properties you want in the specific view. In this case we have the Device Under Test stuff, a select list and a HardwareId.
public class DeviceUnderTestViewModel
{
public int pkDeviceUnderTest { get; set; }
[Required]
public string nkDeviceUnderTest { get; set; }
[Required]
public string nkNotes { get; set; }
public int HardwareId { get; set; } //pkHardware
public IEnumerable<SelectListItem> HardwareSelectList { get; set; } //dropdown of Hardware
}
In the controller, populate the select lists and attach to the viewmodel:
public ActionResult Index()
{
DeviceUnderTestViewModel vm = new DeviceUnderTestViewModel();
vm.HardwareSelectList = db.Hardware.ToList().Select(d => new SelectListItem()
{
Text = d.nkHardware,
Value = d.pkHardware.ToString()
});
// populate all the other dropdowns
return View();
}
In the view we render the select list:
<div class="form-group">
#Html.LabelFor(model => model.HardwareId)
#Html.DropDownListFor(model => model.HardwareIdId, Model.HardwareSelectList, "Select Hardware...", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.HardwareIdId, "", new { #class = "text-danger" })
</div>
DropDownListFor takes the value to set first, here we are setting the hardwareid which we will save to the DeviceUnderTest as the pkHardware, then we have the populate list of Hard items for the dropdown, third is an optional string place holder, then lastly I add a bootstrap class for formatting.
Then return the model to the controller and save it:
public ActionResult SaveDevice(DeviceUnderTestViewModel model)
{
using (var db = new dbContext())
{
DeviceUnderTest device = new DeviceUnderTest()
{
pkHardware = model.HardwareId,
// continue with other items
};
db.Entry<device>.State = EntiyState.Added;
db.SaveChanges();
}
return View();
}

MVC Razor POST - list object is always null

I've tried several times to get my complex object which contains a list to POST correctly. All the properties except the list object contain values when it posts. I've based my approach on this SO Q&A and this SO Q&A. However, in both case the solution is to use a for loop instead of a foreach loop. I'm using the recommended for loop but still have\ing an issue. I'm not sure what else could be causing the list to be null when posting. Thanks in advance for your help!
View:
#model PropertiesAdminSite.Models.UtilityData
#{
ViewBag.Title = "CreateNewCycle";
}
<h2>New Residence Utilities</h2>
#using (Html.BeginForm("Upload", "ImportWater", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="control-group">
#Html.TextBoxFor(m => m.UploadFile, new { type = "file"})
</div>
<div class="control-group">
<input type="submit" class="btn btn-info" value="Upload" />
</div>
<div class="col-lg-12 visible-lg">
<br>
<span style="color:green">#ViewBag.Message</span>
#Html.HiddenFor(model => model.bID)
#Html.HiddenFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bPrevDate)
#for (int i = 0; i < Model.utilData.Count(); i++)
{
#Html.HiddenFor(model => model.utilData[i].ResNumber)
#Html.HiddenFor(model => model.utilData[i].GrnLower)
#Html.HiddenFor(model => model.utilData[i].GrnUpper)
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
#Html.HiddenFor(model => model.utilData[i].rID)
#Html.HiddenFor(model => model.utilData[i].WaterReading)
#Html.HiddenFor(model => model.utilData[i].wDifference)
#Html.HiddenFor(model => model.utilData[i].YelLower)
#Html.HiddenFor(model => model.utilData[i].YelUpper)
}
</div>
}
#using (Html.BeginForm("IndexMulti", "Utilities", FormMethod.Post))
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
#Html.LabelFor(model => model.bEntryDate, htmlAttributes: new { #class = "control-label col-md-1" })
#Html.DisplayFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bID)
#Html.HiddenFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bPrevDate)
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div class="dataTable_wrapper">
<!--div id="dataTables-example_wrapper" class="dataTables_wrapper form-inline dt-bootstrap no-footer">-->
<div class="row">
<div class="col-sm-12">
<table class="table table-striped table-bordered table-hover dataTable no-footer" id="dataTables-Bills" role="grid" aria-describedby="dataTables-example_info">
<!-- /table headers-->
<thead>
<tr role="row">
<th>#Html.DisplayNameFor(model => model.utilData.First().ResNumber)</th>
<th>#Html.DisplayNameFor(model => model.utilData.First().WaterReading)</th>
<th>
#Html.DisplayNameFor(model => model.utilData.First().prevWaterReading)
#Html.DisplayFor(model => model.bPrevDate)
</th>
<th>#Html.DisplayNameFor(model => model.utilData.First().wDifference)</th>
<th>Actions</th>
</tr>
</thead>
<!-- /table body-->
<tbody>
#for (int i = 0; i < Model.utilData.Count(); i++)
{
<tr role="row">
<td>
#Html.DisplayFor(modelItem => modelItem.utilData[i].ResNumber)
#Html.HiddenFor(model => model.utilData[i].GrnLower)
#Html.HiddenFor(model => model.utilData[i].GrnUpper)
#Html.HiddenFor(model => model.utilData[i].YelLower)
#Html.HiddenFor(model => model.utilData[i].YelUpper)
</td>
<td>
#Html.EditorFor(model => model.utilData[i].WaterReading)
</td>
<td>
<span id="#string.Format("prevWater_{0}",Model.utilData[i].rID)">
#Html.DisplayFor(model => model.utilData[i].prevWaterReading)
</span>
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
</td>
<td>
<span id="#string.Format("hdifference_{0}",Model.utilData[i].rID)">
#Html.DisplayFor(model => model.utilData[i].wDifference)
</span>
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
</td>
<td>
#Html.ActionLink("View History", "ExportDataIndex", "ExportData", new { rID = Model.utilData[i].rID, bId = Model.bID }, null) |
<a href="#Url.Action("ExportToExcel", "ExportData", new { rID = Model.utilData[i].rID, bId = Model.bID })" class="btn btn-success">
<i class="fa fa-file-excel-o" aria-hidden="true" title="Export to Excel"></i>
</a> |
<a href="#Url.Action("ChartData", "Utilities", new { rID = Model.utilData[i].rID, bId = Model.bID })" class="btn btn-info">
<i class="fa fa-bar-chart" aria-hidden="true" title="Water Usage History"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-0 col-md-8">
<input type="submit" value="Submit Water Readings and Create Invoices" class="btn btn-primary btn-lg btn-block" />
</div>
</div>
}
Model:
public partial class UtilityData
{
public DateTime bEntryDate { get; set; }
public string bPrevDate { get; set; }
public int bID { get; set; }
//public int residenceCount { get; set; }
public List<UtilEntry> utilData { get; set; }
public HttpPostedFileBase UploadFile { get; set; }
}
public partial class UtilEntry
{
public int rID { get; set; }
public long? WaterReading { get; set; }
public int ResNumber { get; set; }
public long? prevWaterReading { get; set; }
public decimal wDifference { get; set; }
public int GrnUpper { get; set; }
public int GrnLower { get; set; }
public int YelUpper { get; set; }
public int YelLower { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexMulti([Bind(Include = "bEntryDate, bPrevDate,bID,uData")] UtilityData uData)
{
if (ModelState.IsValid)
{
Omitted for clarity
}
return RedirectToAction("Create", "TenantAccount", new { id = uData.bID});
}
Per #Stephen Muecke I had a typo in the IndexMulti Binding:
public ActionResult IndexMulti([Bind(Include = "bEntryDate, bPrevDate,bID,utilData")] UtilityData uData)
utilData was udata originally.

MVC dynamic data passing to controller

I am creating a MVC application. I am having a view with a form to fill, but it seems that controller does not get data that was entered (it is null when debugging).
My controller:
public ActionResult AddGroupsQty(AddGroupsQtyViewModel value)
{
var model = new AddGroupsQtyViewModel();
model.subject_id = value.subject_id;
model.qty = value.qty;
ClassDeclarationsDBEntities1 entities1=new ClassDeclarationsDBEntities1();
var subj = entities1.Subjects
.Where(b => b.class_id == model.subject_id)
.FirstOrDefault();
model.subject_name = subj.name;
if (ModelState.IsValid)
{
int maxId = 0;
int total = 0;
total = entities1.Groups.Count();
if (total == 0)
{
maxId = 0;
}
else
{
maxId = entities1.Groups.Max(u => u.group_id);
}
for (int i = 0; i < value.qty; i++)
{
var teacher = entities1.Users
.Where(b => b.email.Replace(" ", String.Empty) == model.teacher_emails[i].Replace(" ", String.Empty))
.FirstOrDefault();
var group=new Models.Group(value.subject_id, maxId+1, model.group_names[i], teacher.user_id);
entities1.Groups.Add(group);
entities1.SaveChangesAsync();
}
return RedirectToAction("OperationSuccess", "Account");
}
return View(model);
}
My View model:
public class AddGroupsQtyViewModel
{
public int qty { get; set; }
public int subject_id { get; set; }
public string subject_name { get; set; }
[Required]
[Display(Name = "Name of group")]
public List<string> group_names { get; set; }
[Required]
[Display(Name = "Email of teacher")]
public List<string> teacher_emails { get; set; }
}
and the View:
#using System.IdentityModel.Configuration
#using System.Web.UI.WebControls
#model ClassDeclarationsThsesis.Models.AddGroupsQtyViewModel
#{
ViewBag.Title = "AddGroupsQty";
}
<h2>Add Groups to #Model.subject_name</h2>
#if (Model != null)
{
using (Html.BeginForm("AddGroupsQty", "Account", new { qty = Model.qty, Model.subject_id }, FormMethod.Post, new { #class = "form-horizontal", role = "form", }))
{
#Html.AntiForgeryToken()
<h4>Insert data</h4>
<hr />
<table>
<tr>
<th>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.group_names, new { #class = "col-md-2 control-label" })
</div>
</th>
<th>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.teacher_emails, new { #class = "col-md-2 control-label" })
</div>
</th>
</tr>
#for (int i = 0; i < Model.qty; i++)
{
<tr>
<th>
<div class="form-group">
<div class="col-md-10">
#Html.TextBoxFor(m => m.group_names[i], new { #class = "form-control" })
</div>
</div>
</th>
<th>
<div class="form-group">
<div class="col-md-10">
#Html.TextBoxFor(m => m.teacher_emails[i], new { #class = "form-control" })
</div>
</div>
</th>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
}
The problem is that when running the application, filling the form and submitting by pressing the button, it throws a null exception in controller that both lists:
List teacher_emails
and:
List group_names
are null. I do not see any error in my form. How can I solve it?
model.teacher_emails and model.group_names are always null, because model is new.
var model = new AddGroupsQtyViewModel();
Try using value.teacher_emails and value.group_names instead.
Also tidy that code up. A blank line after the variable declaration, and you would have spotted it yourself.

Create and Index in same view using partial view

Ok I give up after several hours of troubleshooting, but I am sure you all will see a solution right away. I have three models Person, Course and CoursePreference. The CoursePreference model has two foreign keys PersonId and CourseId.
What I want to do:
I want to create a view where the user can add course preferences in a top Create section, and when they click Add, the form would post and refresh the List in the same view. Essentially I am combining Index and Create in one view. So I created an Index View and a partial view called _CreatePartial in the CoursePreference folder.
The problem:
The view displays fine, but with two problems. 1) The CoursePreferenceId field shows a dropdownlist. I want it to be a hidden field since it's an identity. I copied the code exactly from the scaffolded Create View, which hides the Id correctly. Dont know why it's not working in the _CreatePartial view? 2) Most importantly, my _CreatePartial will not add any course preference. It looks as if the form is posting but no record is added.
What gives?
Here are the models, controllers and views:
---------------------------
Models (stripped down versions)
---------------------------
public class CoursePreference
{
public int CoursePreferenceId { get; set; }
public Nullable<int> CourseId { get; set; }
public Nullable<int> PersonId { get; set; }
public virtual Course Course { get; set; }
public virtual Person Person { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public virtual ICollection<CoursePreference> CoursePreferences { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public Nullable<int> ProgramId { get; set; }
public string Name { get; set; }
public virtual ICollection<CoursePreference> CoursePreferences { get; set; }
}
------------------
Controllers
------------------
public ActionResult _CreatePartial()
{
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
ViewBag.PersonId = new SelectList(db.People, "PersonId", "LastName");
return View("_CreatePartial");
}
public ActionResult Index()
{
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
ViewBag.PersonId = new SelectList(db.People, "PersonId", "LastName");
var coursepreferences = db.CoursePreferences.Include(c => c.Course).Include(c => c.Person);
return View(coursepreferences.ToList());
}
---------------------------
Index View
---------------------------
#model IEnumerable<MyProj.Models.CoursePreference>
#{ ViewBag.Title = "Index";
}
<h4>Add Course Preferences</h4>
<div>
#Html.Partial("~/Views/CoursePreference/_CreatePartial.cshtml", new MyProj.Models.CoursePreference())
</div>
<br />
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Course.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Person.LastName)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Course.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Person.LastName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CoursePreferenceId }) |
#Html.ActionLink("Details", "Details", new { id=item.CoursePreferenceId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CoursePreferenceId })
</td>
</tr>
}
</table>
---------------------------
_CreatePartial View
---------------------------
#model MyProj.Models.CoursePreference
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CoursePreference</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.CoursePreferenceId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CoursePreferenceId)
#Html.ValidationMessageFor(model => model.CoursePreferenceId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CourseId, "CourseId", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CourseId", String.Empty)
#Html.ValidationMessageFor(model => model.CourseId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PersonId, "PersonId", new { #class = "control-label col-md-2" }
<div class="col-md-10">
#Html.DropDownList("PersonId", String.Empty)
#Html.ValidationMessageFor(model => model.PersonId)
</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>
Nothing is saved because you don't have a controller method to handle the post. Implement this in your controller:
[HttpPost]
public ActionResult Index(CoursePreference pref)
{
// Insert code to fetch database context into variable "db"
db.CoursePreferences.Add(pref);
db.SaveChanges();
return RedirectToAction("Index");
}
As for the hidden ID-field, you should use:
#Html.HiddenFor(model => model.CoursePreferenceId)
-not EditorFor or LabelFor.

Categories