How to get RadioButton values from form? - c#

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

Related

Microsoft.Data.SqlClient.SqlException - Invalid object name 'GuestList'.'

I'm having a big issue with my Database, I cannot write to it from the web page. I am using Dapper as my ORM if that makes a difference?
db.Insert(guest.EditableGuest); is giving me the invalid object error but if I change it to db.InsertAsync(guest.EditableGuest); the error resolves itself BUT I cannot get it to add to the DB and it will not be displayed on the page where I have a list of guests in the DB for some reason?
I decided to retype the page again in case there was an issue, VS seems to be a bit finicky sometimes, one day I had an error, closed the app, reopened it and it was fine? Didn't work with this unfortunately.
GuestListController
using Dapper.Contrib.Extensions;
using FYP_RSVP_MGMT.Helpers;
using FYP_RSVP_MGMT.Models;
using FYP_RSVP_MGMT.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
namespace FYP_RSVP_MGMT.Controllers
{
public class GuestListController : Controller
{
public IActionResult Index()
{
GuestListViewModel guest = new GuestListViewModel();
return View("Index", guest);
}
/* Create or Update a guest RSVP Response */
public IActionResult CreateUpdate(GuestListViewModel guest)
{
if (ModelState.IsValid)
{
using (var db = DbHelpers.GetConnection())
{
/* If a guestID is null, the number of existing guests will be counted
* in order to determine what the next guestID will be and will be added
* asynchronously to the DB in case other actions are on going at the same time */
if (guest.EditableGuest.GuestID == null)
{
guest.EditableGuest.GuestID = guest.Guests.Count;
db.Insert<GuestList>(guest.EditableGuest);
}
/* If the guest already exists, we are updating their details */
else
{
GuestList dbItem = db.Get<GuestList>(guest.EditableGuest.GuestID);
TryUpdateModelAsync<GuestList>(dbItem, "EditableGuest");
db.Update<GuestList>(dbItem);
}
}
/* When a guest submits their RSVP response, it will bring them to the View Guests page - TEMPORARY MEASURE */
return RedirectToAction("ViewGuestList", guest);
}
else
{
return View("ViewGuestList", new GuestList());
}
}
There is more code for Edit and Delete beyond this and the page is throwing no errors so that's all fine.
This is my DB Helper class
public class DbHelpers
{
public static SqlConnection GetConnection()
{
return new SqlConnection(#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WeddingDB;");
}
}
GuestList Model
public class GuestList
{
[ExplicitKey]
public int? GuestID { get; set; }
[Required]
public string GuestName { get; set; }
[Required]
public string GuestType { get; set; }
[Required]
public string ContactDetails { get; set; }
public bool PlusOne { get; set; }
public string PlusOneName { get; set; }
[Required]
public string GuestResponse { get; set; }
}
GuestListViewModel
public class GuestListViewModel
{
/* Creating a new List of all Guests
Editable Guest will hold any instance of an object from the list
that is being added/edited/deleted etc */
public List<GuestList> Guests { get; set; }
public GuestList EditableGuest { get; set; }
/* Selecting all of the existing guests from the DB */
public GuestListViewModel()
{
using (var db = DbHelpers.GetConnection())
{
this.EditableGuest = new GuestList();
this.Guests = db.Query<GuestList>("Select * From GuestList").ToList();
}
}
}
GuestList View
#model FYP_RSVP_MGMT.ViewModels.GuestListViewModel
#{
ViewData["Title"] = "Guest RSVP";
}
#* Form to submit RSVP Response *#
#using (var form = Html.BeginForm("CreateUpdate", "GuestList", FormMethod.Post))
{
<div class="container" id="GuestRSVP">
<br />
<br />
<br />
#* The Bride and Groom name plus Wedding Details will eventually change
depending on the log in details *#
<h3> Welcome to [Bride] and [Groom]'s Wedding</h3>
<h4>[Church Location]</h4>
<h4>[Wedding Date and Time]</h4>
<div class="container" id="RSVPTable" style="align-content:center">
<table>
<tr>
<td>Guest Name: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.GuestName)</td>
</tr>
<tr>
<td>Guest Type: </td>
<td>#Html.DropDownListFor(m => m.EditableGuest.GuestType, new List<SelectListItem> { new SelectListItem { Value = "Guest", Text = "Guest" }, new SelectListItem { Value = "Wedding Party", Text = "Wedding Party" } })</td>
</tr>
<tr>
<td>Email Address: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.ContactDetails)</td>
</tr>
<tr>
<td>Plus One: </td>
<td>#Html.CheckBoxFor(m => m.EditableGuest.PlusOne)</td>
</tr>
<tr>
<td>Plus One Name: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.PlusOneName)</td>
</tr>
<tr>
<td>RSVP Response:</td>
<td>#Html.DropDownListFor(m => m.EditableGuest.GuestResponse, new List<SelectListItem> { new SelectListItem { Value = "Accept", Text = "Accept with Pleasure" }, new SelectListItem { Value = "Decline", Text = "Decline with Regret" } })</td>
</tr>
</table>
<br/>
<button class="btnSubmitRSVP" type="submit" style="text-align:center"> <a asp-controller="GuestList" asp-action="CreateUpdate"></a> #(Model.EditableGuest.GuestID > 0? "Update": "Submit Response") </button>
</div>
</div>
}
Where I am sending some of the pages is probably a bit odd, like a guest RSVPing shouldn't be taken to the page where they can see the other guests and their response but this is just temporary. Could this be affected my code, surely not? Nowhere in my code is there GuestLists so I cannot understand where it is picking this up? I can provide more code if required.
Thank you for your help in advance!
Edit: Here is a picture of the database
Dapper is assuming a pluralized table name. Use [Table("GuestList")] attribute in your GuestList model class.

Post List of lists of RadioButtons MVC

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");
}

Should I use ViewBag to pass a list to a View?

Not sure why I got down voted but I'm going to re-write my question after doing some research and testing. This is a side project that I'm using to learn MVC/EF/Repository/Bootstrap etc. I only get a couple hours here a few nights a week to work on it.
Basic original question:
I know I should really be using a List<> in a ViewModel to pass the data to the View but I'm not sure how or if it will meet my requirement.
What I am trying to do is get a list of users to display in a table which would have a checkbox in each row. Above that table I want to have a list of Groups to that they could be assigned to. You select the section from the DropDownList (DDL) and then check who you want to assign it to. It's the groups/sections that I want want to assign as a list and pass to the View.
So, I've got a ViewModel with a list and I'm using a repository to populate the VM. What I don't know how to do exactly is where/when to populate that list with each VM object and even if I do and there are say 50 users I wouldn't want to make 50 trips to the DB to pull the same information.That is why I'm thinking that for this scenario using the ViewBag to pass that Group list to the View might be justifiable. On the flip side I would like to learn how to populate that list properly in the VM for future coding.
Updated question/code:
So, after more research and following some suggestions I've now got the following code. I'm still not sure how I will properly populate my Patrols in my ViewModel in order to populate the DDL in my View.
At the moment I've got the View displaying the table with the checkboxes. Now I'm back to working on getting the values to populate the DDL and then I'll have to work on posting to the controller, looping to find the check rows, and updating the database. In my case each member record is defaulted to a PatrolId=0 and this page should allow me to update the PatrolId to a value from the DDL.
The Patrols property in the PatrolMemberViewModel should be a list of about 5 records that I would pull from a DB table instead of hard coding in the DDL.
ViewModel:
public class PatrolViewModel
{
public int PatrolId { get; set; }
public string PatrolName { get; set; }
}
public class PatrolMemberViewModel
{
[Key]
public int MemberId { get; set; }
public int PatrolId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Phone")]
public string PhonePrimary { get; set; }
[Display(Name = "Email")]
public string EmailPrimary { get; set; }
public bool IsSelected { get; set; }
public PatrolViewModel Patrols { get; set; }
}
Controller:
public ViewResult Unassigned()
{
try
{
IEnumerable<PatrolMemberViewModel> model = repository.SelectAllUnassigned();
return View(model);
}
catch (Exception)
{
ModelState.AddModelError(string.Empty, "Error retrieving the record.");
return View();
}
}
Repository:
public IEnumerable<PatrolMemberViewModel> SelectAllUnassigned()
{
using (DataContext db = new DataContext())
{
var results = (from p in db.Person
where p.IsActive == true
&& p.IsScout == true
&& p.PatrolId == 0
select new PatrolMemberViewModel
{
MemberId = p.PID,
FirstName = p.FirstName ?? string.Empty,
LastName = p.LastName ?? string.Empty,
EmailPrimary = p.EmailPrimary ?? string.Empty,
PhonePrimary = p.PhonePrimary ?? string.Empty,
PatrolId = p.PatrolId,
IsSelected = false
}
).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return results;
}
}
View:
#model IList<ProjectName.ViewModels.PatrolMemberViewModel>
#{
ViewBag.Title = "Unassigned";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Patrols</h2>
#using (Html.BeginForm("Update", "Patrol", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "alert alert-danger" })
<table class="table table-bordered table-striped table-hover table-condensed tbackground">
<tr>
<th class="text-center">
</th>
<th class="text-center">
First Name
</th>
<th class="text-center">
Last Name
</th>
<th class="text-center">
Email
</th>
<th class="text-center">
Phone
</th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td class="text-center">
#Html.CheckBoxFor(m => m[i].IsSelected)
</td>
<td>
#Html.DisplayFor(m => m[i].FirstName)
</td>
<td>
#Html.DisplayFor(m => m[i].LastName)
</td>
<td>
#Model[i].EmailPrimary
</td>
<td class="text-center">
#Html.DisplayFor(m => m[i].PhonePrimary)
</td>
</tr>
}
</table>
<div class="control-wrapper">
<input type="submit" id="btnSubmit" value="Assign" class="btn btn-success" />
</div>
}
<p> </p>
Start by creating the view models to represent what you want to display/edit in the view. Your PatrolMemberViewModel can be used but remove the [Key] attribute and the int PatrolId and PatrolViewModel Patrols properties.
Then create the parent view model
public class AssignPatrolViewModel
{
[Display(Name = "Patrol")]
[Required(ErrorMessage = "Please select a patrol")]
public int? SelectedPatrol { get; set; }
public IEnumerable<SelectListItem> PatrolList { get; set; }
public List<PatrolMemberViewModel> Members { get; set; }
}
and you GET method would be
public ViewResult Unassigned()
{
var model = new AssignPatrolViewModel
{
PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName"), // modify to suit
Members = repository.SelectAllUnassigned().ToList()
};
return View(model);
}
and in the view
#model AssignPatrolViewModel
....
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.SelectedPatrol)
#Html.DropDownListFor(m => m.SelectedPatrol, Model.PatrolList, "Please select")
#Html.ValidationMessageFor(m => m.SelectedPatrol)
<table>
.... // thead elements
<tbody>
#for(int i = 0; i < Model.Members.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => m.Members[i].IsSelected)
#Html.HiddenFor(m => m.Members[i].MemberId)
// add other hidden inputs for properties you want to post
</td>
<td>#Html.DisplayFor(m => m.Members[i].FirstName)</td>
....
</tr>
}
</tbody>
</table>
<input type="submit" value="Assign" class="btn btn-success" />
}
Then in the POST method
[HttpPost]
public ViewResult Unassigned(AssignPatrolViewModel model)
{
if (!ModelState.IsValid)
{
model.PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName");
return View(model);
}
// Get selected ID's
IEnumerable<int> selected = model.Members.Where(m => m.IsSelected).Select(m => m.MemberId);
// Get matching data models
var members = db.Person.Where(p => selected.Contains(p.PID)); // modify to suit
// loop each each member, update its PatrolId to the value of model.SelectedPatrol
// save and redirect
}
You could create a new class for your view model, with the list of users and the list of sections as properties within it. Some people seem to like that approach.
But I think your use of ViewBag for passing the list of sections is perfectly valid. I do that all the time for DDLs like this.

Model Binding on dynamic created DropDownList

Model1:
public class Model1
{
public int Id { get; set; }
public int VariantId { get; set; }
public string Name { get; set; }
public string Language { get; set; }
public IList<Model2> ListModel2{ get; set; }
public VmSysVariantResource()
{
ListModel2=new List<Model2>();
}
}
Model2:
public class Model2
{
public int Id { get; set; }
public int ParamterId { get; set; }
public int ValueId { get; set; }
[Required]
public string SelectedValue { get; set; }
public string ParameterName { get; set; }
public IList<PossibleValue> Values { get; set; }
public IList<SelectListItem> ValuesSelectListItem
{
get
{
var list = (from item in Values
select new SelectListItem()
{
Text = item.ValueName,
Value = item.Id
}).ToList();
return list;
}
set { }
}
}
PossibleValue:
public class PossibleValue
{
public int Id { get; set; }
public string ValueName { get; set; }
}
Now explication about what I want to do:
Suppose we have one Model "Model1". This model have a list of Model2 .
Model2 contains ParameterName and a list of possibleValues.
Now I have form like this:
#using (Ajax.BeginForm("Action", "Controller", null,
new AjaxOptions
{
HttpMethod = "POST"
}))
{
<table class="sample" style="margin: 0 auto; width: 400px">
<tr>
<td>
#Resource.Name:
</td>
<td> #Html.TextBoxFor(c => c.Name)
</td>
</tr>
#foreach (var item in Model.ParamtersToValue)
{
<tr>
<td>
#item.Parameter:
</td>
<td>
#Html.DropDownList("ParamtersToValue", #item.ValuesSelectListItem)
</td>
</tr>
}
</table>
<input type="submit" value="Submit"/>
}
On post method in controller I want to get Model1 with List of ListModel2.
This is my post method:
[HttpPost]
public ActionResult MyAction(Model1 obj)
{ }
I was expected to get on post method Model1 with ListModel2 that will contain SelectedValue chosen from dropdown, but I am not shure how to construct dropdown inside foreach block to enforce model binding to work.
Any suggestion will be appreciated.Thanks..
The default model binder works with indexes. So, to have it model bind, change your foreach loop to a for loop and render the control with an index. Like this:
#for (int i = 0; i < Model.ParamtersToValue.Count; i++) {
#* .. stuff here *#
<td>
#Html.DropDownListFor(x => Model.ParametersToValue[i], Model.ParametersToValue[i].ValuesSelectListItem)
</td>
#* .. stuff here *#
}
This will result in element names such as [0].ParametersToValue, [1].ParametersToValue, etc. The model binder will bind them in order of appearance into an enumerable of your choice.

Submit a form with a collection of a Model

I have a ViewModel which contains a collection of type of my Model, like so:
public class OrderConfirm
{
public ICollection<QuoteLine> SalesLines { get; set; }
public string Currency { get; set; }
public int EnquiryID { get; set; }
}
My QuoteLine Model looks like so:
public class QuoteLine
{
public int QuoteLineId { get; set; }
public int LostReasonId { get; set; }
public virtual LostReason LostReason { get; set; }
public string ItemName { get; set; }
}
In my View, I then Iterate through each of these QuoteLines, within a form, like so:
#using (Ajax.BeginForm("ConfirmLostOrder", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "LostOrders",
OnBegin = "LostOrderConfirm"
}))
{
<table class="completed-enq-table">
<tr>
<th>
Item Number
</th>
<th>
Reason
</th>
</tr>
#foreach (var sales in Model.SalesLines)
{
<tr>
<td>#sales.ItemName
#Html.HiddenFor(model => sales.QuoteLineID)
</td>
<td>#Html.DropDownListFor(model => sales.LostReasonId, ((IEnumerable<myApp.Models.LostReason>)ViewBag.LostReasons).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.LostReason),
Value = option.LostReasonId.ToString(),
Selected = (Model != null) && (option.LostReasonId == sales.LostStatusId)
}))
</td>
</tr>
}
</table>
<input type="submit" style="float: right;" value="Submit Lost Order" />
}
Then my HttpPost action looks like so:
[HttpPost]
public ActionResult ConfirmLostOrder(List<QuoteLine> models)
{
// process order
return PartialView("Sales/_ConfirmLostOrder");
}
The problem is, models is null. If I use a FormCollection I can see each of the values submitted but I'd like to use my model and not a FormCollection as I'd like to process and edit each of the line submitted individually as they may have different reason's
You can't use a foreach in this instance, it needs to be a for loop so that the name attributes of the fields contain the correct index so that default model binding knows it's binding to a list.
Firstly, I'm going to move your dropdown values out of the ViewBag (they should really be in there). That'll also take out some of that nasty logic in your view :)
So your model is now:
public class OrderConfirm
{
public List<QuoteLine> SalesLines { get; set; }
public string Currency { get; set; }
public int EnquiryID { get; set; }
public SelectList LostReasons { get; set; }
}
Try this instead of your foreach:
#for (var i = 0; i < Model.SalesLines.Count; i++)
{
<tr>
<td>
#Model.SalesLines[i].ItemName
#Html.HiddenFor(m => m.SalesLines[i].QuoteLineId)
#Html.HiddenFor(m => m.SalesLines[i].ItemName) //assuming you want this
</td>
<td>
#Html.DropDownListFor(m => m.SalesLines[i].LostReasonId, Model.LostReasons)
</td>
</tr>
}
Then change your post method to take your OrderConfirm model type:
[HttpPost]
public ActionResult ConfirmLostOrder(OrderConfirm model)

Categories