Here is my code:
Controller:
public ActionResult InsertData(CoModel coModel)
{
if (ModelState.IsValid)
{
db.Entry(coModel).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(coModel);
}
Model:
public class CoModel
{
[Key]
public int id { get; set; }
public string item_no { get; set; }
public string destination { get; set; }
public int total_piece { get; set; }
}
View:
#using (Html.BeginForm("InsertData", "ControllerName", FormMethod.Post, new { Id = "Form1"}))
{
#foreach (var item in Model)
{
#Html.DisplayFor(modelItem => item.item_no) #Html.HiddenFor(model => item.item_no)
#Html.DisplayFor(modelItem => item.destination) #Html.HiddenFor(model => item.destination)
#Html.DisplayFor(modelItem => item.total_piece) #Html.HiddenFor(model => item.total_piece)
<button type="submit">Save</button>
}
}
Here is my question. Why every time I pressed the Save button the controller receives null/zero value? Is my coding wrong?
You are using DisplayFor that is hidden. You must use EditorFor, TextBox, TextBoxFor, TextArea and others that render input HTML type different of hidden.
Related
I have an Attendance program in which I want to assign Students to AttendanceTakers. I am using a table where the headers are the AttendanceTakers and the rows are Students and each cell has a RadioButton. It is basically a double array of RadioButtons. My problem is I can't get it to post.
My AttendanceTaker class
public class SessionAttendanceTaker
{
public int Id { get; set; }
[ForeignKey("Session")]
public int SessionId { get; set; }
public Session Session { get; set; }
[Display(Name="Attendance Taker")]
[ForeignKey("User")]
public string AttendanceTakerId { get; set; }
[Display(Name = "Attendance Taker")]
public User User { get; set; }
public List<Student> Students { get; set; }
}
And the Student that is in the course class
public class StudentSession
{
public int Id { get; set; }
[ForeignKey("Session")]
[DisplayName("Session")]
public int SessionId { get; set; }
public Session Session { get; set; }
[ForeignKey("Student")]
[DisplayName("Student")]
public int StudentId { get; set; }
public Student Student { get; set; }
[DisplayName("Credits Awarded")]
public int Credit { get; set; }
}
Student class
public class Student
{
public int Id { get; set; }
[ForeignKey("User")]
public string UserId { get; set; }
[DisplayName("Name")]
public virtual User user { get; set; }
public Student()
{
}
}
The View
#using (Html.BeginForm())
{
<div class="form-horizontal">
<table>
<thead>
<tr>
<th> Name </th>
#{
foreach (var attendanceTaker in Model.SessionAttendanceTakers)
{
<th>#attendanceTaker.User.LastName, #attendanceTaker.User.FirstName </th>
}
}
</tr>
</thead>
<tbody>
#{
//See https://stackoverflow.com/questions/7667495/mvc-radiobuttons-in-foreach to try and clean the foreach
foreach (var studentSession in Model.StudentSessions)
{
<tr>
<td>
#studentSession.Student.User.LastName, #studentSession.Student.User.FirstName
</td>
#foreach (var attendanceTaker in Model.SessionAttendanceTakers)
{
#Html.EditorFor(Model => Model.SessionAttendanceTakers, "StudentsToAttendanceTakersModel", "" + studentSession.StudentId, new { htmlAttributes = new { #class = "form-control" } })
}
</tr>
}
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Assign" class="btn btn-default" />
</div>
</div>
</div>
}
and EditorTemplate
#model IEnumerable<SessionAttendanceTaker>
#using Attendance.Models
<td>
#Html.RadioButtonFor(model => model, new { htmlAttributes = new { #class = "form-control" } })
</td>
As an aside I would love to get rid of the foreaches as per this post but since I don't know how many attendance takers or students there will be until runtime I can't figure out how to do that besides for just moving them to the Editor and I don't see a point to that.
Also the Controller
[HttpPost]
public ActionResult Assign(StudentsToAttendanceTakersModel model)
{
return RedirectToAction("Index");
}
I have a breakpoint on the return and the attendanceTakers is null and Student sessions has a count of 0.
Additionally, using FormCollection
public ActionResult Assign(FormCollection o)
only gives me the Students who's RadioButton was clicked but not the AttendanceTaker. If more info is needed let me know. Thanks.
EDIT
Model
public class StudentsToAttendanceTakersModel
{
public IEnumerable<StudentSession> StudentSessions { get; set; }
public IEnumerable<SessionAttendanceTaker> SessionAttendanceTakers { get; set; }
public StudentsToAttendanceTakersModel() { }
}
You're creating radio buttons which do not relate to your model, and you're trying to bind them to a complex object (SessionAttendanceTaker) - a radio button posts back a simple value (and you are not even giving the radio buttons a valid value - the 2nd parameter of RadioButtonFor() is the value).
You are editing data, so you should start by creating view models which represent what you want to display in the view.
public class StudentVM
{
public int ID { get; set; }
public string Name { get; set; }
[Required(ErrorMessage = "Please select an attendance taker")]
public int? SelectedAttendanceTaker { get; set; }
}
public class AttendanceTakerVM
{
public int ID { get; set; }
public string Name { get; set; }
}
public class StudentAttendanceTakersVM
{
public List<StudentVM> Students { get; set }
public IEnumerable<AttendanceTakerVM> AttendanceTakers { get; set; }
}
So that your view will be
#model StudentAttendanceTakersVM
....
#using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Student</th>
#foreach(var taker in Model.AttendanceTakers)
{
<th>#taker.Name</th>
}
<th></th>
</tr>
</thead>
<tbody>
#for(int i = 0; i < Model.Students.Count; i++)
{
<tr>
<td>
#Model.Students[i].Name
#Html.HiddenFor(m => m.Students[i].ID)
#Html.HiddenFor(m => m.Students[i].Name)
</td>
#foreach(var taker in Model.AttendanceTakers)
{
<td>#Html.RadioButtonFor(m => m.Students[i].SelectedAttendanceTaker, taker.ID, new { #class = "form-control" })</td>
}
<td>#Html.ValidationMessageFor(m => m.Students[i].SelectedAttendanceTaker)</td>
</tr>
}
</tbody>
</table>
<input type="submit" ... />
}
Your GET method will then initialize an instance of you view model and pass it to the view, for example, for a 'Create' method
public ActionResult Create()
{
var students = db.Students.Select(x => new StudentVM
{
ID = x.Id,
Name = x.User.FirstName + " " + x.User.LastName // adjust as required
}).ToList();
var attendanceTakers = db.SessionAttendanceTakers.Select(x => new AttendanceTakerVM
{
ID = x.Id,
Name = x.User.FirstName + " " + x.User.LastName // adjust as required
});
StudentAttendanceTakersVM model = new StudentAttendanceTakersVM
{
Students = students,
AttendanceTakers = attendanceTakers
};
return View(model);
}
And the POST method will be
public ActionResult Create(StudentAttendanceTakersVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// loop through model.Students to get the ID of the Student and its selected AttendanceTaker
// initialize the data models and save to the database
return RedirectToAction("Index");
}
I have a radio button list with an IList View Model in C# MVC 5. My ViewModel values are passed to controller Action Result method.
However, the webpage allows the user to select multiple radio buttons. What I need to how do I select individual button for my list items (one at a time).
Here's the screen for selected radio buttons:
Here's my ViewModel:
public class DeliveryDateVM
{
public int Id { get; set; }
public bool SelectedItem { get; set; }
public string DeliveryDay { get; set; }
public string DeliveryType { get; set; }
}
Here's my View:
#model IList<ViewModels.DeliveryDateVM>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#for (var i = 0; i < Model.Count; i++) {
#Html.HiddenFor(x => x[i].Id)
#{var uniqueID = Model[i].Id;}
<tr>
<td>
#{var uniqueID = Model[i].Id;}
#Html.RadioButtonFor(model => Model[i].SelectedItem, true, new { id = uniqueID })
</td>
<td>
#Html.DisplayFor(x => x[i].DeliveryType)
#Html.HiddenFor(x => x[i].DeliveryType)
</td>
<td>
#Html.DisplayFor(x => x[i].DeliveryDay)
#Html.HiddenFor(x => x[i].DeliveryDay)
</td>
</tr>
}
<button type="submit" class="btn btn-primary">Submit</button>
}
The controller values pass screen:
Here's my GET Controller:
public ActionResult DeliveryDates()
{
var model = db.DeliveryPeriods
.Select(c =>
new DeliveryDateVM()
{
Id = c.Id,
DeliveryDay = c.DeliveryDay,
DeliveryType = c.DeliveryType,
}).ToList();
return View(model);
}
Radio buttons need to be grouped by name and your giving each radio button a different name attribute.
Change you view models to
public class MainVM // rename as required
{
public string SelectedDay { get; set; }
public List<DeliveryDateVM> Days { get; set; }
}
public class DeliveryDateVM
{
public int Id { get; set; }
public string DeliveryDay { get; set; }
public string DeliveryType { get; set; }
}
so that you view is
#model MainVM
....
#for (var i = 0; i < Model.Days.Count; i++)
{
#Html.RadioButtonFor(m => m.SelectedDay, Model.Days[i].DeliveryDay, new { id = "" })
#Html.DisplayFor(m => m.Days[i].DeliveryType)
#Html.HiddenFor(m => m.Days[i].DeliveryType)
....
}
This will now generate all radio buttons with name="SelectedDay" and the value of SelectedDay when you post back to your model will be the value of the DeliveryDay (i.e. "Monday" or "Tuesday" etc)
Side note: You may want to consider changing the DeliveryDay and SelectDay properties to a DayOfWeek enum and also create your own enum for DeliveryType.
Based on your comments, the revised get method would be
MainVM model = new MainVM
{
SelectedDay = "Monday", // set this if you want a default button selected
Days = db.DeliveryPeriods.Select(c => new DeliveryDateVM()
{
Id = c.Id,
DeliveryDay = c.DeliveryDay,
DeliveryType = c.DeliveryType,
}).ToList()
};
return View(model);
When clicking save to submit my form, my HTTPPost Edit Action receives a null value for IEnumerable<MedicalProduct> productList. I am not sure what parameter to use for my edit Action to prevent nulls. I would try and determine the cause with some breakpoints but cannot find the spot before the parameter is assigned null.
Side Note:
I am adapting my code to use this new Model/ViewModel hierarchy. My Controller hasn't been completely tested with these new models, but I started testing the Edit Action, and received the null reference exception when trying to use the parameter IEnumerable<MedicalProduct> productList in my Edit post action.
Another Side Note:
I am using a sub-ViewModel class MedicalProductViewModelLineItem (haven't figured out a better name yet) inside my ViewModel MedicalProductViewModel because I need a way to retrieve all the Brand names from the database with one database call, and then assign them one by one to the MedicalProductViewModelLineItem.
EDIT: CODE UPDATE 10/22/13 5:14pm CST. The values produced in FormCollection.Keys parameter in the HttpPost Edit action method are now fixed. Now, values like "Products[0].Name" or "Products[0].ID" are produced in FormCollection.Keys parameter instead of "p.Name" or "Name". However, the productList parameter is still null.
Model Classes
MedicalProductViewModel
public class MedicalProductViewModel
{
public List<MedicalProductViewModelLineItem> Products { get; private set; }
//public SelectListItem BrandSelectListItem { get; private set; }
public void BuildViewModel(IEnumerable<MedicalProductViewModelLineItem> productsList, IEnumerable<Brand> brandList)
{
BuildProducts(productsList, brandList);
}
public void BuildViewModel(IEnumerable<MedicalProduct> productsList, IEnumerable<Brand> brandList)
{
BuildProducts(productsList, brandList);
}
private IEnumerable<SelectListItem> BuildSelectListItems(IEnumerable<Brand> brandList)
{
return brandList.Select(b => new SelectListItem()
{
Text = b.Name,
Value = b.ID.ToString()
});
}
private void BuildProducts(IEnumerable<MedicalProductViewModelLineItem> productList, IEnumerable<Brand> brandList)
{
var medicalProducts = productList.Select(p => new MedicalProduct()
{
BrandID = p.BrandID,
ID = p.ID,
Name = p.Name,
Price = p.Price
});
BuildProducts(medicalProducts, brandList);
}
private void BuildProducts(IEnumerable<MedicalProduct> productsList, IEnumerable<Brand> brandList)
{
Products = productsList.Select(p => new MedicalProductViewModelLineItem()
{
BrandID = p.BrandID,
BrandName = brandList.Single(b => b.ID == p.BrandID).Name,
BrandSelectListItem = BuildSelectListItems(brandList),
ID = p.ID,
Name = p.Name,
Price = p.Price
}).ToList();
}
}
MedicalProductViewModelLineItem
// Sub-ViewModel of MedicalProductViewModel
// It gets displayed as one row on a view.
public class MedicalProductViewModelLineItem
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
// is a foreign key
public int BrandID { get; set; }
public string BrandName { get; set; }
}
MedicalProduct
// DOMAIN MODEL
public class MedicalProduct
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
// is a foreign key
public int BrandID { get; set; }
}
Controller
MedicalProductController
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb()
//
// GET: /MedicalSupply/Edit/5
public ActionResult Edit(int id = 0)
{
MedicalProduct product = _db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
var productList = new List<MedicalProduct> { product };
var viewModel = GetMedicalProductViewModel(productList);
return View(viewModel);
}
// ==========================================
// NULL REFERENCE EXCEPTION OCCURS IN THIS ACTION
// ==========================================
// POST: /MedicalSupply/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IEnumerable<MedicalProductViewModelLineItem> productList, FormCollection values)
{
if (ModelState.IsValid)
{
foreach (var product in productList)
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var productViewModelList = GetMedicalProductViewModel(productList);
return View(productViewModelList);
}
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}
Views
Edit.cshtml
#model MvcMedicalStore.Models.MedicalProductViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#for (int i = 0; i < Model.Products.Count(); i++)
{
#Html.EditorFor(m => m.Products[i])
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
EditorTemplates\MedicalProductViewModelLineItem.cshtml
#model MvcMedicalStore.Models.MedicalProductViewModelLineItem
#Html.HiddenFor(item => Model.ID)
<div class="editor-label">
#Html.LabelFor(item => Model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Name)
#Html.ValidationMessageFor(item => Model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Price)
#Html.ValidationMessageFor(item => Model.Price)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.BrandID)
</div>
<div class="editor-field">
#Html.DropDownListFor(item => Model.BrandID, Model.BrandSelectListItem)
#Html.ValidationMessageFor(item => Model.BrandID)
</div>
EDIT: (Pictures obsolete)
two images of the foreach approach, and for approach used in MedicalProductViewModel.cshtml, and the resulting key values of FormsCollection parameter values
Use a BindAttribute with Prefix in your Controller Action like below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Prefix = "Products")] IEnumerable<MedicalProductViewModelLineItem> productList)
{
if (ModelState.IsValid)
{
foreach (var product in productList)
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var productViewModelList = GetMedicalProductViewModel(productList);
return View(productViewModelList);
}
Change your view from foreach loop to for loop
#foreach (MvcMedicalStore.Models.MedicalProductViewModelLineItem p in Model.Products)
{
#Html.HiddenFor(item => p.ID)
//rest of the code.
}
to
#for ( i = 0; i < Model.count(); i++)
{
#Html.LabelFor(m => m[i].Name)
#Html.HiddenFor(m => m[i].Name)
#Html.LabelFor(m => m[i].Price)
#Html.EditorFor(m => m[i].Price)
//rest of the code.
}
[HttpPost]
public MedicalProductViewModel GetMedicalProductViewModel(ICollection<MedicalProduct> productList)
{
var brandList = _db.Brands.ToArray();
var mapper = new MedicalProductMapper();
return mapper.MapMedicalProductViewModel(productList, brandList);
}
Make it more explicit, specify
FormMethod.Post
#using (Html.BeginForm()) { } to
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { id = "forId" }))
{}
Edit: Removed Partial View to make things simpler. Now I just need to find out why The View isn't Posting the Values
ViewModelProspectUsers
public class ViewModelProspectUsers
{
public int Id { get; set; }
public string User { get; set; }
public IEnumerable<ViewModelProspectSelect> Prospects { get; set; }
}
ViewModelProspectSelect
public class ViewModelProspectSelect
{
public int ProspectID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
View
#model OG.ModelView.ViewModelProspectUsers
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.Id)
<h5>Please Select Prospects you wish to assign to this User.</h5>
-----HERE is where the partial used to be, these values aren't being posted to the [Post] Method------
-----------------------------------------However they are populating just fine----------------------------------------
#foreach (var item in Model.Prospects)
{
#Html.HiddenFor(x => item.ProspectID)
#Html.DisplayFor(x => item.Name)
#Html.EditorFor(x => item.IsSelected)
}
#*#Html.Partial("_ShowProspectCheckedForUser", Model.Prospects)*#
#*#Html.Partial("_ShowProspectCheckedForuser", new OG.ModelView.ViewModelProspectSelect())*#
<input type="submit" value="Save changes" />
#Html.ActionLink("Cancel", "Index")
}
Post
[HttpPost]
public ActionResult UsersInProspect(ViewModelProspectUsers viewModel)
If i were to look at viewModel.Prospects(m=>m.isSelected) //<- this value is Null shouldn't be
My viewmodel Variableis showing Data but not for the Ienumerable.
When dealing with list-type objects, you must reference them with array notation to have the field names generated in a way that the modelbinder can parse them back, i.e:
for (var i = 0; i < Model.Count(); i++)
{
#Html.LabelFor(m => Model[i].SomeProperty)
#Html.EditorFor(m => Model[i].SomeProperty)
}
In your scenario, you'd be better served by using a view model to contain your list and adding a Selected property to the items so that you can track which ones were or were not selected.
public class ViewModelProspects
{
public List<ViewModelProspectSelect> Prospects { get; set; }
}
public class ViewModelProspectSelect
{
// Whatever else you have
public bool Selected { get; set; }
}
Then, in your view:
#model ViewModelProspects
#using (Html.BeginForm())
{
for (var i = 0; i < Model.Prospects.Count(); i++)
{
<label>
#Html.HiddenFor(m => Model.Prospects[i].Id)
#Html.CheckboxFor(m => Model.Prospects[i].Selected, true)
#Model.Prospects[i].Name
</label>
}
}
And finally, change your action method signature:
[HttpPost]
public ActionResult UsersInProspect(ViewModelProspects model)
Then, you can easily get the list of selected ids inside the action with:
var selectedIds = model.Prospects.Where(m => m.Selected).Select(m => m.Id)
I am having trouble getting the value of the selected item from a drop down list.
Apologies for how much code I've posted, but it is very simple code to read.
When I edit a vehicle my Get Edit method returns the vehicle and the dropdown list has the correct VehicleType selected in the view.
BUT
When my [HttpPost] Edit gets the model back, the the VehicleType is null, the other fields, name, description are correctly updated.
I have a class called Vehicle and one called VehicleType
public class Vehicle
{
public string Name { get; set; }
public string Description { get; set;
public virtual VehicleType VehicleType { get; set; }
}
public class VehicleType
{
public int VehicleTypeID { get; set; }
public string VehicleTypeDescription { get; set; }
}
in my controller I have an get and set edit methods
public ActionResult Edit(int id = 0)
{
Vehicle myVehicle = db.Vehicle.Find(id);
if (myVehicle == null)
{
return HttpNotFound();
}
PopulateVehicleTypeList(myVehicle.VehicleType.VehicleTypeID);
return View(myVehicle);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Vehicle myVehicle)
{
if (ModelState.IsValid)
{
db.Entry(myVehicle).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(myVehicle);
}
And my view
//View
#model Vehicle
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Vehicle</legend>
#Html.HiddenFor(model => model.VehicleID)
<div class="editor-label">
#Html.LabelFor(model => model.VehicleType.VehicleTypeDescription)
</div>
<div class="editor-field">
#Html.DropDownList("VehicleTypeID", String.Empty)
#Html.ValidationMessageFor(model => model.VehicleType.VehicleTypeID)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
There's a few things you should do.
Firstly, create a view Model which has Vehicles and VehicleTypes
public class VehicleViewModel
{
public IEnumerable<SelectListItem> VehicleTypes { get; set; }
public Vehicle vehicle { get; set; }
}
Secondly, in your view , change to DropDownListFor:
#Html.DropDownListFor(
x => x.Vehicle.VehicleType,
Model.VehicleTypes,
"-- Select a vehicle type --", null)
Your controller will look something like this:
VehicleViewModel viewModel = new VehicleViewModel
{
Vehicle vehicle = <populate via Linq query>
VehicleTypes = VehicleTypes.Select(x => new SelectListItem
{
Value = x.VehicleTypeID,
Text = VehicleTypeDescription
}).ToList(),
vehicle = vehicle
};
Hope that helps.