View not passing Ienumerable of Model - c#

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)

Related

Controller receives null/zero value from a View in MVC4

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.

Get selected radiobutton values in MVC

I have below Model structure
public class Quiz
{
public List<Question> Questions { get; set; }
}
public class Question
{
public int Id { get; set; }
public String QuestionText { get; set; }
public List<Option> Options { get; set; }
public int AnswerId { get; set; }
}
public class Option
{
public int Id { get; set; }
public String OptionText { get; set; }
public int DisplayOrder { get; set; }
}
And my view is like below where I am displaying all the questions and options
foreach (var question in Model.Questions)
{
#Html.DisplayFor(modelItem => question.QuestionText) <br />
foreach (var option in question.Options)
{
#Html.RadioButton("Id", option.Id)#option.OptionText
}
}
<input type="submit" value="Submit" />
I am not getting the values for all selected radiobuttons, It always returns one value in form collection
[HttpPost]
public ActionResult Quiz(Quiz userResponse, FormCollection form)
{
foreach (var item in form.AllKeys)
{
string value = form[item];
// save data
}
//var selectedOption = frm["Id"];
return View();
}
Can you please help?
You creating radio buttons that have no relationship to you model and all have the same name="Id" attribute (and you would only ever be able to select one option from all the questions). You need to use a for loop or EditorTemplate for typeof Question (refer this answer for a more detailed explanation). Using an EditorTemplate, your code will be
/Views/Shared/EditorTemplates/Question.cshtml
#model Question
#Html.HiddenFor(m => m.Id)
#Html.DisplayFor(m => m.QuestionText)
#foreach(var option in Model.Options)
{
<div>
<label>
#Html.RadioButtonFor(m => m.AnswerId , option,Id, new { id = "" })
<span>#option.OptionText</span>
</label>
</div>
}
and in the main view
#model Quiz
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.Questions)
<input type="submit" value="Save" />
}
and the controller method
[HttpPost]
public ActionResult Quiz(Quiz model)
The model in the POST method will be bound with each question containing its Id and AnswerId (the Id of the selected Option) properties.
Side note: Your current POST method has return View() but this would fail because the value of each Options property will be null. If you need to return the view (which should only be necessary if ModelState is invalid), then you need to repopulate those collections.

Radio button list with IList View Model in C# MVC 5

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

MVC editorfor not working for collection

I have a model class called Events and a model class called EventRange:
public class Event
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<EventRange> RangesCollection { get; set; }
}
public class EventRange
{
public int Id { get; set; }
public string RangeName { get; set; }
public string RangeDescription { get; set; }
public int Capacitiy { get; set; }
}
As you can see the Event class contains a List for as many EventRanges as the user should be able to add a lot of EventRanges.
I have created a view called events, which dynamicly appends a partial view for the evenrange. The user can in example press the Add Event Range button 5 times if he want's 5 EventRanges saved.
The Event view:
#using (Ajax.BeginForm("CreateEvent", "Events", new AjaxOptions { HttpMethod = "POST" }, new { #class = "mainForm" }))
{
#*Event data:*#
#Html.LabelFor(m => m.Name)#Html.TextBoxFor(m => m.Name)
#Html.LabelFor(m => m.Description)#Html.TextBoxFor(m => m.Description)
#*EventRange data:*#
<div id="EventRangediv">
#Html.EditorFor(m => m.RangesCollection)
</div>
}
The partial view for the eventrange is saved under "~/views/events/EditorTemplates/EventRange.cshtml"
EventRange.cshtml:
#model fanaticksMain.Models.EventRange
#Html.HiddenFor(m => m.Id)
#Html.DisplayFor(m => m.RangeName)
#Html.LabelFor(m => m.RangeName)#Html.TextBoxFor(m => m.RangeName)
#Html.LabelFor(m => m.RangeDescription)#Html.TextBoxFor(m => m.RangeDescription)
However, loading the events view doesn't load any element from the partialview, the labels and textboxes for the name and description for the event work. But nothing is shown for the eventrange partial view.
Anyone have anyone idea what I'm doing wrong? Also, does anyone have any suggestion if this is the right way to bind a collection for posting a form?
EventRange.cshtml has the model specified as EventRange. m.RangesCollection is a collection of EventRange objects.
I don't see a model that is defined as accepting a collection of EventRange.
try place a for loop in your code to iterate each EventRange and display the EventRange.cshtml editor for each one:
Instead of:
#Html.EditorFor(m => m.RangesCollection)
Try:
#for (int i = 0; i < m.RangeCollection.Count; i++)
{
#Html.EditorFor(m => m.RangeCollection[i])
}
Try placing the "EventRange.cshtml" in the “~/Views/Shared/EditorTemplates/” folder.

.net mvc4 HttpPost null value

I have a problem while passing an object with HttpPost...
Once the form is submitted, the model is set "null" on the controller side, and I don't know where is the issue..
Here is my controller :
public ActionResult AddUser(int id = 0)
{
Group group = db.Groups.Find(id);
List<User> finalList = db.Users.ToList() ;
return View(new AddUserTemplate()
{
group = group,
users = finalList
});
//Everything is fine here, the object is greatly submitted to the view
}
[HttpPost]
public ActionResult AddUser(AddUserTemplate addusertemplate)
{
//Everytime we get in, "addusertemplate" is NULL
if (ModelState.IsValid)
{
//the model is null
}
return View(addusertemplate);
}
Here is AddUserTemplate.cs :
public class AddUserTemplate
{
public Group group { get; set; }
public User selectedUser { get; set; }
public ICollection<User> users { get; set; }
}
Here is the form which return a null value to the controller (note that the dropdown list is greatly populated with the good values) :
#using (Html.BeginForm()) {
<fieldset>
<legend>Add an user</legend>
#Html.HiddenFor(model => model.group)
#Html.HiddenFor(model => model.users)
<div class="editor-field">
//Here, we select an user from Model.users list
#Html.DropDownListFor(model => model.selectedUser, new SelectList(Model.users))
</div>
<p>
<input type="submit" value="Add" />
</p>
</fieldset>
}
Thanks a lot for your help
I tried your code and in my case the addusertemplate model was not null, but its properties were all null.
That's because of a few model binding issues: Html.HiddenFor and Html.DropDownListFor do not work with complex types (such as Group or User) (at least that's how it is by default).
Also, Html.HiddenFor cannot handle collections.
Here's how to solve these issues:
instead of #Html.HiddenFor(model => model.group) there should be one #Html.HiddenFor for each property of the group that you need bound
instead of #Html.HiddenFor(model => model.users) you need to iterate through the list of users and for each object add #Html.HiddenFor for each property of the user that you need bound
instead of #Html.DropDownListFor(model => model.selectedUser [...], create a property like int SelectedUserId {get;set;} and use that in the DropDownList (as it cannot handle complex types).
Here's the code that works:
1. The User and Group classes, as I imagined them to be:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
}
2. The adjusted AddUserTemplate class:
public class AddUserTemplate
{
public Group Group { get; set; }
public IList<User> Users { get; set; }
public int SelectedUserId { get; set; }
public User SelectedUser
{
get { return Users.Single(u => u.Id == SelectedUserId); }
}
}
The adjustments:
Users was changed from ICollection to IList, because we'll need to access elements by their indexes (see the view code)
added SelectedUserId property, that will be used in the DropDownList
the SelectedUser is not a readonly property, that returns the currently selected User.
3. The adjusted code for the view:
#using (Html.BeginForm())
{
<fieldset>
<legend>Add an user</legend>
#*Hidden elements for the group object*#
#Html.HiddenFor(model => model.Group.Id)
#Html.HiddenFor(model => model.Group.Name)
#*Hidden elements for each user object in the users IList*#
#for (var i = 0; i < Model.Users.Count; i++)
{
#Html.HiddenFor(m => m.Users[i].Id)
#Html.HiddenFor(m => m.Users[i].Name)
}
<div class="editor-field">
#*Here, we select an user from Model.users list*#
#Html.DropDownListFor(model => model.SelectedUserId, new SelectList(Model.Users, "Id", "Name"))
</div>
<p>
<input type="submit" value="Add" />
</p>
</fieldset>
}
Another option that does not require a bunch of hidden fields is to simply specify that you want the model passed to the controller. I think this is much cleaner.
#using(Html. BeginForm("action","controller", Model, FormMethod.Post)){
...
}

Categories