I have a form that writes out an Approve/Deny radio button for each record. I'm trying to figure out how to use the HttpPost to loop through each and determine if the radio button is selected and if so, which one was selected.
Doing some research I see that some use the Form collection for the form and in one example I found where the user used the forms ViewModel (which is what I normally do). However, when I try either one I'm coming up empty handed.
Here is my form. I'm writing out each record in a List to a table. I've tried both the Html.RadioButton and Html.RadioButtonFor to create them. I also have a comments textbox underneath the table where someone can put in some comments. Here is a snippet of the View.
<tbody>
#foreach (var item in Model.listPendingExceptions)
{
<tr>
<td>#Html.RadioButton("rdo" + item.RID, "A")</td>
<td>#Html.RadioButton("rdo" + item.RID, "D")</td>
<td>#item.Shift_Date.ToShortDateString()</td>
</tr>
}
</tbody>
#Html.TextAreaFor(m => m.ExceptionComment, new { cols = 200, #rows = 4, #maxlength = "100", #class = "form-control", #placeholder = "100 character limitation", #autofocus = "autofocus" })
In my HttpPost I've tried using the form collection. However, what I've found is to look in the AllKeys list. When I view my Post the only thing in the AllKeys is the comment's TextBox value.
When I use a ViewModel in the HttpPost, the list of exceptions that I used to populate the table in the View is NULL. I would expect that since I didn't store the list in a hidden field.
How can I loop through each record, determine which if any radio button has been selected, as well as get the text from the Comments textbox?
UPDATE for EditTemplate
I created the folder structure for EditorTemplates in the Views.
I already had a ViewModel with a List of Exceptions but I did move the SelectedApproval from the main VM to the list of Exceptions.
public class ReportPendingExceptionsViewModel
{
public List<PendingException> listPendingExceptions { get; set; }
public bool IsAdmin { get; set; }
[Required(ErrorMessage = "*Required")]
public string ExceptionComment { get; set; }
}
public class PendingException
{
public int RID { get; set; }
public DateTime Shift_Date { get; set; }
public string Shift_Text { get; set; }
public string Emp_Id { get; set; }
public string Emp_Name { get; set; }
public string Last_Name { get; set; }
public string First_Name { get; set; }
public string Comment_Text { get; set; }
public string SelectedApproval { get; set; }
}
I then created a Razor View for the Table rows.
#model ProjectName.Models.ViewModels.PendingException
<tr>
<td>#Html.RadioButtonFor(e=>e.SelectedApproval,"A")</td>
<td>#Html.RadioButtonFor(e => e.SelectedApproval, "D")</td>
<td>#Model.Shift_Date.ToShortDateString()</td>
<td>#Model.Emp_Name</td>
<td>#Model.Shift_Text</td>
<td>#Model.Comment_Text</td>
<td></td>
</tr>
I then updated my main View to use the EditFor.
<thead>
<tr>
<th style="width:80px;">Approve</th>
<th style="width:80px;">Deny</th>
<th>Shift Date</th>
<th>Employee</th>
<th>Schedule</th>
<th>Comments</th>
<th></th>
</tr>
</thead>
<tbody>
#Html.EditorFor(f => f.listPendingExceptions)
</tbody>
However, when I run it, all I get is the RID values. So, I must be missing something. Here is the output from the View Source.
Did I miss a step?
public class ExceptionModel
{
public int Id { set; get; }
public bool IsApproved { set; get; }
public DateTime ShiftDate { set; get; }
}
public class MainModel
{
public string Comment { set;get;}
public List<ExceptionModel> lst_Exception { set;get;}
}
//this is get request action method
public ActionResult Create()
{
MainModel model = new MainModel();
model.lst_Exception = new List<ExceptionModel>()
{
new ExceptionModel() {Id = 1,IsApproved = false, ShiftDate = DateTime.Now},
new ExceptionModel() {Id = 2,IsApproved = false, ShiftDate = DateTime.Now},
new ExceptionModel() {Id = 3,IsApproved = false, ShiftDate = DateTime.Now}
};
return View(model);
}
//this is view for action method
#model MainModel
#using(Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Approve</th>
<th>Deny</th>
<th>Shift Date</th>
</tr>
</thead>
<tbody>
#for (var item = 0; item < Model.lst_Exception.Count(); item++)
{
<tr>
<td>#Html.RadioButtonFor(model=>model.lst_Exception[item].IsApproved, "Approve")</td>
<td>#Html.RadioButtonFor(model=>model.lst_Exception[item].IsApproved, "Deny")</td>
<td><span>#Model.lst_Exception[item].ShiftDate</span>
#Html.HiddenFor(model => model.lst_Exception[item].ShiftDate})
</td>
</tr>
}
</tbody>
</table>
#Html.TextBoxFor(model=>model.Comment)
<input type="Submit" value="Submit" />
}
//this is Post action method
[HttpPost]
public ActionResult Create(MainModel model)
{
//here you can loop through model.lst_Exception to get the select values
//from the view
}
It is very easy to do this with Editor Templates.
Start with creating a view model for pending exception items
public class ExceptionVm
{
public int Id { set; get; }
public bool? IsApproved { set; get; }
public DateTime ShiftDate { set; get; }
}
and in your main view model, you will add a collection property which of of type
List<ExceptionVm>.
public class MyViewModel
{
public string Comment { set;get;}
public List<ExceptionVm> PendingExceptions { set;get;}
public MyViewModel()
{
PendingExceptions = new List<ExceptionVm>();
}
}
And in your GET action you initialize the view model object, load the PendingExceptions property
public ActionResult Create()
{
var vm = new MyViewModel();
vm.ExceptionVms = new List<ExceptionVm>()
{
new ExceptionVm() {Id = 1, ShiftDate = DateTime.Now.AddDays(-3)},
new ExceptionVm() {Id = 2, ShiftDate = DateTime.Now.AddDays(-2)},
new ExceptionVm() {Id = 3, ShiftDate = DateTime.Now.AddDays(-1)}
};
return View(vm);
}
Now, let's create an editor template. Create a new directory called EditorTemplates under ~/Views/YourControllerName/ or ~/Views/Shared/ and add a new razor view under that. Give the file the same name as our view model class, ExceptionVm.cshtml
Now add the below code to the editor template view. This basically render the 2 radio buttons and the date
#model ExceptionVm
<tr>
<td>#Html.RadioButtonFor(b=>b.IsApproved, true)</td>
<td>#Html.RadioButtonFor(b => b.IsApproved, false) </td>
<td> #Model.ShiftDate #Html.HiddenFor(x=>x.Id) </td>
</tr>
Now go to your main view, which is strongly typed to our MyViewModel class, and call the Html.EditorFor helper method and pass the PendingExceptions collection property to that
#model MyViewModel
#using(Html.BeginForm())
{
<table class="table">
<tbody>
#Html.EditorFor(f => f.PendingExceptions)
</tbody>
</table>
#Html.TextBoxFor(f => f.Comment)
<input type="Submit" value="Submit" class="btn btn-default" />
}
The call to the EditorFor will render a table row for each item in the PendingExceptions collection. When you submit the form, you can use the same MyViewModel class as the parameter and inspect the PendingExceptions property, iterate through each item and see whether it is true or false or null(if they have not selected anything)
[HttpPost]
public ActionResult Create(MyViewModel model)
{
// check model.PendingExceptions collection and each items IsApproved prop value
// to do : return something
}
If you do not want to allow null selection, change the IsApproved property type from bool? to bool
I have dropdownlist, which I have filled from database. Now I need to get the selected value in Controller do some manipulation. But not getting the idea. Code which I have tried.
Model
public class MobileViewModel
{
public List<tbInsertMobile> MobileList;
public SelectList Vendor { get; set; }
}
Controller
public ActionResult ShowAllMobileDetails()
{
MobileViewModel MV = new MobileViewModel();
MV.MobileList = db.Usp_InsertUpdateDelete(null, "", "", null, "", 4, MergeOption.AppendOnly).ToList();
MV.Vendor = new SelectList(db.Usp_VendorList(), "VendorId", "VendorName");
return View(MV);
}
[HttpPost]
public ActionResult ShowAllMobileDetails(MobileViewModel MV)
{
string strDDLValue = ""; // Here i need the dropdownlist value
return View(MV);
}
View
<table>
<tr>
<td>Mobile Manufacured</td>
<td>#Html.DropDownList("ddlVendor", Model.Vendor, "Select Manufacurer") </td>
</tr>
<tr>
<td>
</td>
<td>
<input id="Submit1" type="submit" value="search" />
</td>
</tr>
</table>
1st Approach (via Request or FormCollection):
You can read it from Request using Request.Form , your dropdown name is ddlVendor so pass ddlVendor key in the formCollection to get its value that is posted by form:
string strDDLValue = Request.Form["ddlVendor"].ToString();
or Use FormCollection:
[HttpPost]
public ActionResult ShowAllMobileDetails(MobileViewModel MV,FormCollection form)
{
string strDDLValue = form["ddlVendor"].ToString();
return View(MV);
}
2nd Approach (Via Model):
If you want with Model binding then add a property in Model:
public class MobileViewModel
{
public List<tbInsertMobile> MobileList;
public SelectList Vendor { get; set; }
public string SelectedVendor {get;set;}
}
and in View:
#Html.DropDownListFor(m=>m.SelectedVendor , Model.Vendor, "Select Manufacurer")
and in Action:
[HttpPost]
public ActionResult ShowAllMobileDetails(MobileViewModel MV)
{
string SelectedValue = MV.SelectedVendor;
return View(MV);
}
UPDATE:
If you want to post the text of selected item as well, you have to add a hidden field and on drop down selection change set selected item text in the hidden field:
public class MobileViewModel
{
public List<tbInsertMobile> MobileList;
public SelectList Vendor { get; set; }
public string SelectVendor {get;set;}
public string SelectedvendorText { get; set; }
}
use jquery to set hidden field:
<script type="text/javascript">
$(function(){
$("#SelectedVendor").on("change", function {
$("#SelectedvendorText").val($(this).text());
});
});
</script>
#Html.DropDownListFor(m=>m.SelectedVendor , Model.Vendor, "Select Manufacurer")
#Html.HiddenFor(m=>m.SelectedvendorText)
Model
Very basic model with Gender field. GetGenderSelectItems() returns select items needed to populate DropDownList.
public enum Gender
{
Male, Female
}
public class MyModel
{
public Gender Gender { get; set; }
public static IEnumerable<SelectListItem> GetGenderSelectItems()
{
yield return new SelectListItem { Text = "Male", Value = "Male" };
yield return new SelectListItem { Text = "Female", Value = "Female" };
}
}
View
Please make sure you wrapped your #Html.DropDownListFor in a form tag.
#model MyModel
#using (Html.BeginForm("MyController", "MyAction", FormMethod.Post)
{
#Html.DropDownListFor(m => m.Gender, MyModel.GetGenderSelectItems())
<input type="submit" value="Send" />
}
Controller
Your .cshtml Razor view name should be the same as controller action name and folder name should match controller name e.g Views\MyController\MyAction.cshtml.
public class MyController : Controller
{
public ActionResult MyAction()
{
// shows your form when you load the page
return View();
}
[HttpPost]
public ActionResult MyAction(MyModel model)
{
// the value is received in the controller.
var selectedGender = model.Gender;
return View(model);
}
}
Going further
Now let's make it strongly-typed and enum independent:
var genderSelectItems = Enum.GetValues(typeof(Gender))
.Cast<string>()
.Select(genderString => new SelectListItem
{
Text = genderString,
Value = genderString,
}).AsEnumerable();
MVC 5/6/Razor Pages
I think the best way is with strongly typed model, because Viewbags are being aboused too much already :)
MVC 5 example
Your Get Action
public async Task<ActionResult> Register()
{
var model = new RegistrationViewModel
{
Roles = GetRoles()
};
return View(model);
}
Your View Model
public class RegistrationViewModel
{
public string Name { get; set; }
public int? RoleId { get; set; }
public List<SelectListItem> Roles { get; set; }
}
Your View
<div class="form-group">
#Html.LabelFor(model => model.RoleId, htmlAttributes: new { #class = "col-form-label" })
<div class="col-form-txt">
#Html.DropDownListFor(model => model.RoleId, Model.Roles, "--Select Role--", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.RoleId, "", new { #class = "text-danger" })
</div>
</div>
Your Post Action
[HttpPost, ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegistrationViewModel model)
{
if (ModelState.IsValid)
{
var _roleId = model.RoleId,
MVC 6 It'll be a little different
Get Action
public async Task<ActionResult> Register()
{
var _roles = new List<SelectListItem>();
_roles.Add(new SelectListItem
{
Text = "Select",
Value = ""
});
foreach (var role in GetRoles())
{
_roles.Add(new SelectListItem
{
Text = z.Name,
Value = z.Id
});
}
var model = new RegistrationViewModel
{
Roles = _roles
};
return View(model);
}
Your View Model will be same as MVC 5
Your View will be like
<select asp-for="RoleId" asp-items="Model.Roles"></select>
Post will also be same
Razor Pages
Your Page Model
[BindProperty]
public int User User { get; set; } = 1;
public List<SelectListItem> Roles { get; set; }
public void OnGet()
{
Roles = new List<SelectListItem> {
new SelectListItem { Value = "1", Text = "X" },
new SelectListItem { Value = "2", Text = "Y" },
new SelectListItem { Value = "3", Text = "Z" },
};
}
<select asp-for="User" asp-items="Model.Roles">
<option value="">Select Role</option>
</select>
I hope it may help someone :)
If you want to use #Html.DropDownList , follow.
Controller:
var categoryList = context.Categories.Select(c => c.CategoryName).ToList();
ViewBag.CategoryList = categoryList;
View:
#Html.DropDownList("Category", new SelectList(ViewBag.CategoryList), "Choose Category", new { #class = "form-control" })
$("#Category").on("change", function () {
var q = $("#Category").val();
console.log("val = " + q);
});
If you're looking for something lightweight, I'd append a parameter to your action.
[HttpPost]
public ActionResult ShowAllMobileDetails(MobileViewModel MV, string ddlVendor)
{
string strDDLValue = ddlVendor; // Of course, this becomes silly.
return View(MV);
}
What's happening in your code now, is you're passing the first string argument of "ddlVendor" to Html.DropDownList, and that's telling the MVC framework to create a <select> element with a name of "ddlVendor." When the user submits the form client-side, then, it will contain a value to that key.
When MVC tries to parse that request into MV, it's going to look for MobileList and Vendor and not find either, so it's not going to be populated. By adding this parameter, or using FormCollection as another answer has suggested, you're asking MVC to specifically look for a form element with that name, so it should then populate the parameter value with the posted value.
Use SelectList to bind #HtmlDropdownListFor and specify selectedValue parameter in it.
http://msdn.microsoft.com/en-us/library/dd492553(v=vs.108).aspx
Example : you can do like this for getting venderid
#Html.DropDownListFor(m => m.VendorId,Model.Vendor)
public class MobileViewModel
{
public List<tbInsertMobile> MobileList;
public SelectList Vendor { get; set; }
public int VenderID{get;set;}
}
[HttpPost]
public ActionResult Action(MobileViewModel model)
{
var Id = model.VenderID;
I was having the same issue in asp.NET razor C#
I had a ComboBox filled with titles from an EventMessage, and I wanted to show the Content of this message with its selected value to show it in a label or TextField or any other Control...
My ComboBox was filled like this:
#Html.DropDownList("EventBerichten", new SelectList(ViewBag.EventBerichten, "EventBerichtenID", "Titel"), new { #class = "form-control", onchange = "$(this.form).submit();" })
In my EventController I had a function to go to the page, in which I wanted to show my ComboBox (which is of a different model type, so I had to use a partial view)?
The function to get from index to page in which to load the partial view:
public ActionResult EventDetail(int id)
{
Event eventOrg = db.Event.Include(s => s.Files).SingleOrDefault(s => s.EventID == id);
// EventOrg eventOrg = db.EventOrgs.Find(id);
if (eventOrg == null)
{
return HttpNotFound();
}
ViewBag.EventBerichten = GetEventBerichtenLijst(id);
ViewBag.eventOrg = eventOrg;
return View(eventOrg);
}
The function for the partial view is here:
public PartialViewResult InhoudByIdPartial(int id)
{
return PartialView(
db.EventBericht.Where(r => r.EventID == id).ToList());
}
The function to fill EventBerichten:
public List<EventBerichten> GetEventBerichtenLijst(int id)
{
var eventLijst = db.EventBericht.ToList();
var berLijst = new List<EventBerichten>();
foreach (var ber in eventLijst)
{
if (ber.EventID == id )
{
berLijst.Add(ber);
}
}
return berLijst;
}
The partialView Model looks like this:
#model IEnumerable<STUVF_back_end.Models.EventBerichten>
<table>
<tr>
<th>
EventID
</th>
<th>
Titel
</th>
<th>
Inhoud
</th>
<th>
BerichtDatum
</th>
<th>
BerichtTijd
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.EventID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Titel)
</td>
<td>
#Html.DisplayFor(modelItem => item.Inhoud)
</td>
<td>
#Html.DisplayFor(modelItem => item.BerichtDatum)
</td>
<td>
#Html.DisplayFor(modelItem => item.BerichtTijd)
</td>
</tr>
}
</table>
VIEUW: This is the script used to get my output in the view
<script type="text/javascript">
$(document).ready(function () {
$("#EventBerichten").change(function () {
$("#log").ajaxError(function (event, jqxhr, settings, exception) {
alert(exception);
});
var BerichtSelected = $("select option:selected").first().text();
$.get('#Url.Action("InhoudByIdPartial")',
{ EventBerichtID: BerichtSelected }, function (data) {
$("#target").html(data);
});
});
});
</script>
#{
Html.RenderAction("InhoudByIdPartial", Model.EventID);
}
<fieldset>
<legend>Berichten over dit Evenement</legend>
<div>
#Html.DropDownList("EventBerichten", new SelectList(ViewBag.EventBerichten, "EventBerichtenID", "Titel"), new { #class = "form-control", onchange = "$(this.form).submit();" })
</div>
<br />
<div id="target">
</div>
<div id="log">
</div>
</fieldset>
Thanks - this helped me to understand better ansd solve a problem I had.
The JQuery provided to get the text of selectedItem did NOT wwork for me
I changed it to
$(function () {
$("#SelectedVender").on("change", function () {
$("#SelectedvendorText").val($(**"#SelectedVender option:selected"**).text());
});
});
Simple solution not sure if this has been suggested or not. This also may not work for some things. That being said this is the simple solution below.
new SelectListItem { Value = "1", Text = "Waiting Invoices", Selected = true}
List<SelectListItem> InvoiceStatusDD = new List<SelectListItem>();
InvoiceStatusDD.Add(new SelectListItem { Value = "0", Text = "All Invoices" });
InvoiceStatusDD.Add(new SelectListItem { Value = "1", Text = "Waiting Invoices", Selected = true});
InvoiceStatusDD.Add(new SelectListItem { Value = "7", Text = "Client Approved Invoices" });
#Html.DropDownList("InvoiceStatus", InvoiceStatusDD)
You can also do something like this for a database driven select list. you will need to set selected in your controller
#Html.DropDownList("ApprovalProfile", (IEnumerable<SelectListItem>)ViewData["ApprovalProfiles"], "All Employees")
Something like this but better solutions exist this is just one method.
foreach (CountryModel item in CountryModel.GetCountryList())
{
if (item.CountryPhoneCode.Trim() != "974")
{
countries.Add(new SelectListItem { Text = item.CountryName + " +(" + item.CountryPhoneCode + ")", Value = item.CountryPhoneCode });
}
else {
countries.Add(new SelectListItem { Text = item.CountryName + " +(" + item.CountryPhoneCode + ")", Value = item.CountryPhoneCode,Selected=true });
}
}
public class SaleItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SalesDBContext
{
public static List<SaleItem> GetItems()
{
var items = new List<SaleItem>(){
new SaleItem{Id=1,Name="Soap"},
new SaleItem{Id=2,Name="Milk Power"},
new SaleItem{Id=3,Name="Toothpaste"},
new SaleItem{Id=4,Name="Ice Cream"}
};
return items.ToList();
}
}
public class SalesViewModel
{
public string Item { get; set; }
public List<SaleItem> itemlist { get; set; }
}
I have above SalesViewModel class and SalesDBContext for dummy data generation. I want to add items to a list selected from a dropdown. For that I have created the following view:
#model MVCDropdown.Models.SalesViewModel
#using MVCDropdown.Models
<form method="post">
<p>
#Html.DropDownListFor(model => model.Item, new SelectList(ViewBag.Items, "Id", "Name"), "--select--")
<input type="submit" value="Add" />
</p>
<p>
#if (Model.itemlist != null)
{
<table>
#foreach (var s in Model.itemlist)
{
<tr>
<td>#s.Name</td>
</tr>
}
</table>
}
</p>
</form>
The Controller
[HttpGet]
public ActionResult Index()
{
SalesViewModel model = new SalesViewModel
{
Item = "",
itemlist = new List<SaleItem>()
};
PopDrodown();
return View(model);
}
[HttpPost]
public ActionResult Index(SalesViewModel vm)
{
var t = SalesDBContext.GetItems().Where(x => x.Id == Convert.ToInt32(vm.Item)).FirstOrDefault();
vm.itemlist.Add(t);
PopDrodown();
return View(vm);
}
private void PopDrodown()
{
ViewBag.Items = SalesDBContext.GetItems();
}
Items added to the list should be displayed in a table under the dropdown. However, when I post post back a selected item from the dropdown by pressing add, it returns a null itemlist to the controller, and previously added items are not there. How can I avoid this problem?
You only have a single input element inside your HTML form: that's the dropdown. So the only value that is sent to your controller action when the form is submitted is the Item property. If you want to send the collection you could use hidden fields:
<table>
#for (var i = 0; i < Model.itemlist.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(x => x.itemlist[i].Id)
#Html.HiddenFor(x => x.itemlist[i].Name)
#Html.DisplayFor(x => x.itemlist[i].Name)
</td>
</tr>
}
</table>
Obviously if the user is not supposed to edit the values inside the HTML form, then a much better approach is to have a POST view model that will contain only the properties that the user can modify and you will retrieve the collection elements from the same place you retrieved them in the GET action.
When my Model.State is NOT valid I want to return the view WITH the checked checkboxes.
How would you change my code? Is it possible at all with my approach?
VIEW
#model ListTest.Models.PeopleListViewModel
#{
var hasMoreThanOnePerson = #Model.People.Count > 1;
}
#Html.BeginForm("Save", "Home")
{
#Html.ValidationSummary(false)
<table>
#foreach (var item in Model.People)
{
<tr>
#if (hasMoreThanOnePerson)
{
<td>
<input type="checkbox" name="SelectedIds" value="#item.PersonId" />
</td>
}
else
{
#Html.Hidden("SelectedIds", item.PersonId)
}
<td>
<input type="text" value="#item.Name" />
</td>
</tr>
}
</table>
<input type="submit" value="Save" />
}
VIEWMODEL
public class PeopleListViewModel
{
public PeopleListViewModel()
{
SelectedIds = new int[] { };
}
[MinLength(1, ErrorMessage = "Minimum one person must be selected!")]
public int[] SelectedIds { get; set; }
public List<Person> People { get; set; }
}
CONTROLLER
public ActionResult Index()
{
var people = new List<Person> {
new Person { Name = "Horst", PersonId = 10 },
new Person { Name = "Michael", PersonId = 20}
};
return View(new PeopleListViewModel { People = people });
}
[HttpPost]
public ActionResult Save(PeopleListViewModel viewModel)
{
if (ModelState.IsValid)
{
}
viewModel.People = new List<Person> { new Person { Name = "Horst", PersonId = 10 }, new Person { Name = "bernarnd", PersonId = 20 } };
return View("Index", viewModel);
}
Few things to change
Firstly, change your People model to include an IsSelected property, we want to do away with your SelectedIds method
Secondly, in order to post the data from the client, we need to rewrite your foreach to be a for so the fields are indexed correctly, we'll also add some extra HiddenFors for the properties that you want to keep (because we're no longer re-populating your model when validation fails), your table will be:
<table>
#for (int i = 0; i < Model.People.Count; i++)
{
<tr>
#Html.HiddenFor(m => m.People[i].PersonID)
#Html.HiddenFor(m => m.People[i].Name)
#if (hasMoreThanOnePerson)
{
<td>
#Html.CheckBoxFor(m => m.People[i].IsSelected)
</td>
}
else
{
#Html.HiddenFor(m => m.People[i].IsSelected)
}
<td>
<input type="text" value="#Model.People[i].Name" />
</td>
</tr>
}
</table>
Finally, we don't reassign your People list in your action method if validation fails just return the model that was passed in. If you want to get the selected people, use the code I've added below. Also, because we don't have the SelectedIds anymore we can perform our own validation:
[HttpPost]
public ActionResult Save(PeopleListViewModel viewModel)
{
List<People> selected = viewModel.People
.Where(p => p.IsSelected)
.ToList();
if (selected.Any())
{
//it's valid
List<int> selectedIds = selected
.Select(s => s.PersonID)
.ToList();
}
return View("Index", viewModel);
}
I have controller for address I used it to enter multiple addresses but I want to create dropdosnlist to select the person and enter his addresses
I create this helper class in my model folder to create select item
public class PersonsSelectItems
{
public int SelectedId { get; set; }
public List<Person> Persons { get; set; }
}
I use AddressController to send the selectitem to it view
public class AddressController : Controller
{
private readonly CustomersDBEntities context = new CustomersDBEntities();
private PersonsSelectItems personsSelectItems= new PersonsSelectItems();
///get list of persones
///
public List<Person> GetPersonsList()
{
return (from c in personsSelectItems.Persons
select c).ToList();
}
//
// GET: /Address/
public ActionResult Index()
{
//var model = GetPersonsList(); //var model = GetPersonsList().Select(x => new SelectListItem
//{
// Value = x.PersonID.ToString(),
// Text = x.FirstName,
// Selected = true | false
//});
///var model = new PersonsSelectItems { Persons = GetPersonsList() };
var model = GetPersonsList();
return View(model);
}
//
// GET: /Address/Welcome/
public string Welcome()
{
return "This is the Welcome action method...";
}
[HttpPost]
public ActionResult Create(Address address)
{
//Loop through the request.forms
var Addesslist = new List<Address>();
for (int i = 1; i <= Request.Form.Count; i++)
{
var street = Request.Form["street_0" + i + ""];
var city = Request.Form["city_0" + i + ""];
var postalCode = Request.Form["postalCode_0" + i + ""];
var province = Request.Form["province_0" + i + ""];
var personID = 1;
if (street != null && city != null && postalCode != null && province != null)
{
try
{
context.Addresses.Add(new Address
{
Street = street,
City = city,
Province = province,
PostalCode = postalCode,
PersonID = personID
});
context.SaveChanges();
}
catch (Exception exc)
{
}
}
else
{
break;
}
}
return RedirectToAction("Index");
}
}
I get this expsetion
Value cannot be null. Parameter name: source
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: source
Adress view
#model MVC.Models.Address
Tuple<Person,Order>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Create", "Address", FormMethod.Post))
{
#Html.DropDownListFor(x => x.Person, new SelectList(Model.Person, "PersonId", "FirstName"))
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="table-responsive">
<table id="address_table" class="table">
<thead>
<tr>
<th>Street</th>
<th>City</th>
<th>Province</th>
<th>PostalCode</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input id="Text1" type="text" name="street_01" maxlength="255" required class="street" /></td>
<td>
<input id="Text2" type="text" name="city_01" maxlength="255" required class="city" /></td>
<td>
<input id="Text3" type="text" name="province_01" maxlength="255" required class="province" /></td>
<td>
<input id="Text4" type="text" name="postalCode_01" maxlength="7" required class="postalCode" /></td>
<td> </td>
</tr>
</tbody>
</table>
</div>
<input type="button" value="Add Row" id="add_AdressRow" class="btn btn-lg btn-success btn-block" />
<p>
<input type="submit" value="Create" />
</p>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
just want to ask how can I bind dropDwonList using list from GetPersonsList() function
from AdressController and bind I now their are away to do it but I could not find it ?
Your problem is that you're trying to use some LINQ over a null list.
This bad boy here:
public List<Person> Persons { get; set; }
Is null. You can add a constructor to your type to initialize it:
public class PersonsSelectItems
{
public int SelectedId { get; set; }
public List<Person> Persons { get; set; }
public PersonsSelectItems() {
Persons = new List<Person>();
}
}
..and that will stop your current error.
I have to point out a couple of things though. Firstly, the naming Persons is strange. Make it an English plural of People.
Secondly, you don't actually have to use LINQ here. Your GetPersonList method can simply be:
public List<Person> GetPersonsList()
{
return personsSelectItems.Persons;
}
Even then.. you have access to that collection already. So your model assignment can be:
var model = _personsSelectItems.Persons;