Pass Select into Controller via Response - asp.net-mvc

Hy,
I'm new to ASP.NET MVC 5. I'm trying to get the value of an HTML select with no success.
My View (essential part):
<div class="form-group">
#Html.Label("Country", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownList("Countries", (IEnumerable<SelectListItem>)ViewBag.Countries, new { #class = "form-control", id = "Country", name = "Country" })
</div>
</div>
My Controller (essential part):
public ActionResult Index()
{
string country = Request["Country"]; // here I always get null
}
I need a newbie like explanation why this is not working and how I get it to work, please :)

First, I agree with #Maess. Don't use ViewBag. It's horrible and someone at Microsoft should be slapped for ever adding it as an option in the first place.
That said, your error is pretty obvious here. You named your select "Countries" and you're trying to pull "Country" out of the request.
Since you're new, I'll be nice and lay out how to use a view model for this. First, create a model:
public class IndexViewModel
{
public int SelectedCountry { get; set; }
public IEnumerable<SelectListItem> CountryChoices { get; set; }
}
Then in your action:
// GET
public ActionResult Index()
{
var model = new IndexViewModel();
// get your country list somehow
// where `Id` and `Name` are properties on your country instance.
model.CountryChoices = countries.Select(m => new SelectListItem { Value = m.Id, Text = m.Name });
return View(model);
}
And in your view:
#model Namespace.IndexViewModel
...
#Html.DropDownListFor(m => m.SelectedCountry, Model.CountryChoices, new { #class = "form-control" })
And finally, in your POST action:
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
// use model.SelectedCountry
}

Related

Set The selected Value of a Multiselect from Controller to View after submitting an invalid Model

I am using a Multiselect DropDownList to generate a multiple <select>
I was able to generate it and was working fine.
But If I try to submit it using the parameters:
Name = null
ObjAOption = [1,2] // assume I selected 2 options in my multiselect
ObjAOption will just select option value='1' instead of select options 1, and 2.
Is there any way I can get back the selected options and pass it back to my view by setting it in my controller? I would love to use HTML helper and not to use jQuery or javascript on this part.
Controller:
public ActionResult AddObjectA(AddModel am){
if(ModelState.IsValid){
//Save
}
else {
am.ObjA = // new List of ObjectA with atleast 4 option
return View("MyView",am);
}
}
View:
#Html.LabelFor(model => model.ObjA, "Object A")
#Html.DropDownList("ObjAOption", new MultiSelectList(Model.ObjA, "Key", "Name"), "-- Select Object A--", new { #class = "custom-select custom-select-sm", multiple="" })
#Html.ValidationMessageFor(m => m.ObjAOption, "", new { #class = "text-danger" })
Model:
public class AddModel {
[Required]
public String Name {get;set;}
public IEnumerable<ObjectA> ObjA{ get; set; }
[Required(ErrorMessage = "Please select at least one option")]
public List<int>ObjAOption{ get; set; }
}
public class ObjectA {
public int Key {get;set;}
public string Name {get;set;}
}
Have you tried to use the helper Hiddenfor ? It generate a field that keep your element value, name and attribute :
View:
#Html.LabelFor(model => model.ObjA, "Object A")
#Html.DropDownList("ObjAOption", new MultiSelectList(Model.ObjA, "Key", "Name"), "-- Select Object A--", new { #class = "custom-select custom-select-sm", multiple="" })
#Html.ValidationMessageFor(m => m.ObjAOption, "", new { #class = "text-danger" })
#Html.HiddenFor(m => m.ObjAOption)
Solution:
I scrapped out my DropDownList and tried using ListBoxFor as discussed here

Form submitting DropDownListFor with Required field

I have a dropdownlist from a table called StampsCSEMonths_test. Im trying to make the dropdown required before submitting the form.
The problem it always submits the form no matter if I select or not.
What could be the issue?
View:
#model WINHRAdmin.Models.CTS_Stamps
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.DropDownListFor(model => model.stampscsemonths_id, ViewBag.StampsCSEMonths_test as IEnumerable<SelectListItem>, "", new { #class = "form-control " })
#Html.ValidationMessageFor(model => model.stampscsemonths_id, "", new { #class = "text-danger" })
}
Model:
namespace WINHRAdmin.Models
{
public class CommitmentToServiceModel
{
}
public class CTS_Stamps
{
[Required]
[Range(1,Int32.MaxValue,ErrorMessage ="MonthYerar is required")]
public int? stampscsemonths_id { get; set; }
}
}
Controller:
public ActionResult CTS_Stamps()
{
ViewData["StampsCSEMonths_test"] =
new SelectList((from s in _IntranetEntities.StampsCSEMonths_test.OrderByDescending(x => x.stampscsemonths_id).ToList()
select new
{
stampscsemonths_id = s.stampscsemonths_id,
FullName = s.month + "/" + s.year
}),
"stampscsemonths_id",
"FullName",
null);
LoadEmployeer();
return View();
}
[HttpPost]
public ActionResult CTS_Stamps(FormCollection collection)
{
var testvar = collection["stampscsemonths_id"];//after submission it does get the value of ID
return View();
}
Typically you would want to use a client-side library like jquery.validate to prevent form submission if it's invalid. Here is a good article on the subject: https://www.codeproject.com/Articles/718004/ASP-NET-MVC-Client-Side-Validation.
The usual pattern for server-side MVC validation is almost what you're doing. You put the [Required] attribute above your data model attribute. That's good. You have a #ValidationMessageFor element in your view. Also good.
You forgot, however, to check if the model is valid in your controller. Add a check for ModelState.IsValid before you process the results. Something like this:
if (!ModelState.IsValid)
{
return View(collection); // assumes collection is the model
}
If the user submits an invalid form, after the round-trip they will see a validation error message next to the missing field.
Hope that helps.

Dropdownlist value is null after posting to controller in ASP.NET MVC

I can get all Roles plus actually Role for chosed user, but then When I posting to EditUser action, then Dropdownlist sends null.
I mean When the form posts to my controller, I get null from DropDownList.
Here is my Model
public class EditUserViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public List<SelectListItem> ApplicationRoles { get; set; }
public string ApplicationRoleId { get; set; }
}
Here is Action
[HttpGet]
public async Task<ActionResult> EditUser(string id)
{
EditUserViewModel model = new EditUserViewModel();
model.ApplicationRoles = RoleManager.Roles.Select(r => new SelectListItem
{
Text = r.Name,
Value = r.Id
}).ToList();
if (!String.IsNullOrEmpty(id))
{
ApplicationUser user = await UserManager.FindByIdAsync(id);
if (user != null)
{
var role = await UserManager.GetRolesAsync(user.Id);
var existingRole = role.First();
string existingRoleId = RoleManager.Roles.Single(r => r.Name == existingRole).Id;
model.Id = user.Id;
model.FirstName = user.FirstName;
model.ApplicationRoleId = existingRoleId;
ViewBag.RoleId = new SelectList(RoleManager.Roles, "Id", "Name", model.ApplicationRoleId);
}
}
return PartialView("_EditUser", model);
}
And here is DropDownlist from _EditUser.cshtml
<div class="form-group">
#Html.Label("Role typ", htmlAttributes: new { #class = "control-label col-md-6" })
<div class="col-md-12" title="Ange antal datorer som finns i lager">
#Html.DropDownList("RoleId", null, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ApplicationRoles, "", new { #class = "text-danger" })
</div>
</div>
Getting null Only from DropDownList, not from #Html.EditorFor
/Thanks in advance!
Forms post back the name/value pairs of their successful form controls. Your generating a <select> element with name="RoleId" but you model does not contain a property named RoleId. Since you want to bind the selected option to the ApplicationRoleId role property, then you view needs to be
#Html.LabelFor(m => m.ApplicationRoleId)
#Html.DropDownListFor(m => m.ApplicationRoleId, Model.ApplicationRoles)
#Html.ValidationMessageFor(m => m.ApplicationRoleId)
Notes:
Your current #Html.Label(..) code does not create a label
associated with your dropdownlist (clicking on it will not set
focus)
The ValidationMessageFor() need to be applied to the property your
binding to, not the SelectList
Delete you ViewBag.RoleId = new SelectList(..) code. Your have
already assigned the selectlist to the ApplicationRoles property
(and you should never need ViewBag if have a view model anyway)
Because you are declare that only HttpGet methods are allow in that method of the controller. Thats why

Issue with Model Binding

I have created a View Model called CompetitionRoundModel which is partially produced below:
public class CompetitionRoundModel
{
public IEnumerable<SelectListItem> CategoryValues
{
get
{
return Enumerable
.Range(0, Categories.Count())
.Select(x => new SelectListItem
{
Value = Categories.ElementAt(x).Id.ToString(),
Text = Categories.ElementAt(x).Name
});
}
}
[Display(Name = "Category")]
public int CategoryId { get; set; }
public IEnumerable<Category> Categories { get; set; }
// Other parameters
}
I have structured the model this way because I need to populate a dropdown based on the value stored in CategoryValues. So for my view I have:
#using (Html.BeginForm())
{
<div class="form-group">
#Html.LabelFor(model => model.CategoryId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.CategoryId, Model.CategoryValues, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CategoryId, "", new { #class = "text-danger" })
</div>
</div>
// Other code goes here
}
I have selected model.CategoryId in the DropDownListFor() method since I want to bind the selected value to CategoryId. I really don't care for CategoryValues, I just need it to populate the DropDown.
My problem now is that when my Controller receives the values for my Model in the action method, CategoryValues is null which causes the system to throw a ArgumentNullException (the line that is highlighted is the return Enumerable line.
I have even tried [Bind(Exclude="CategoryValues")] but no change at all. Any help would be much appreciated.
Your not (and should not be) creating form controls for each property of each Category in your IEnumerable<Category> collection so in your POST method, the value of Categories is null (it never gets initialized). As soon as you attempt CategoryValues and exception is thrown by your .Range(0, Categories.Count()) line of code in the getter.
Change you view model to give CategoryValues a simple geter/setter, and delete the Categories property
public class CompetitionRoundModel
{
public IEnumerable<SelectListItem> CategoryValues { get; set; }
[Display(Name = "Category")]
public int CategoryId { get; set; }
.... // Other properties
}
and populate the SelectList in the controller methods, for example
var categories db.Categories; // your database call
CompetitionRoundModel model = new CompetitionRoundModel()
{
CategoryValues = categories.Select(x => new SelectListItem()
{
Value = x.Id.ToString(),
Text = x.Name
},
....
};
return View(model);
or alternatively
CompetitionRoundModel model = new CompetitionRoundModel()
{
CategoryValues = new SelectList(categories, "Id", "Name" ),
Note also that if you return the view (because ModelState is invalid, the you need to repopulate the value of CategoryValues (refer The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable' for more detail)
Since CategoryValues just populates the drop down, it will never post back to the server and you'll need to rebuild the list from the database before using it in the GET or POST operation. The CategoryId property is the value that will be posted back to the server from the DropDownList.

MVC 5 Multiselect List Values Not Binding to Model On Post

I have a view with several multiselect lists which are declared like this
<div class="col-md-6">
#Html.LabelFor(model => model.Counties, htmlAttributes: new { #class = "control-label" })
#Html.ListBoxFor(model => model.Counties, new MultiSelectList(ViewBag.CountyList, "Value", "Text"), htmlAttributes: new { #class = "form-control", size = 8, tabindex = 26 })
#Html.ValidationMessageFor(model => model.Counties, "", new { #class = "text-danger" })
<span class="small">Ctrl + click to select multiple items</span>
</div>
My view model contains a declaration like this:
public virtual List<long> Counties { get; protected set; }
My action looks like this:
[HttpPost]
public ActionResult Edit(TScholarshipView model, FormCollection form)
{
if (ModelState.IsValid)
{
TScholarship scholarship = Repo.GetScholarship(model.Id);
scholarship = Mapper.Map<TScholarshipView, TScholarship>(model, scholarship);
Repo.SaveOrUpdate(scholarship, HttpContext.User.Identity.Name);
return RedirectToAction("Edit", "AdminScholarship", new { id = model.Id });
}
return View("Scholarship", model);
}
On submit I can look at the post data sent by the browser and it is sending the appropriate data to the server
...&Counties=16&Counties=34&...
When the action begins to execute the value of Counties in the model is null. However I can look at the FormCollection values and the value of form["Counties"] is "16,34". Any ideas why the binding is not occurring?
I noticed this right after I posted the question. The problem was having the setter protected. This prevented the binder from setting the value of the list.
You need to reset the value of ViewBag.CountyList on post event as well.
Or have one property in the model and bind that property to your multi select list box.
Something like
Wrapper / Model
public class CustomerList
{
public List<Customer> Customers { get; set; }
public List<int> SelectedIDs { get; set; }
}
Controller
[HttpGet]
public ActionResult DisplayCustomer()
{
Customer oCustomer = new Customer();
List<Customer> CustomersList = new List<Customer>();
CustomersList.Add(new Customer() { ID = 1, Name = "TestCustomer1", Amt = 123 });
CustomersList.Add(new Customer() { ID = 2, Name = "TestCustomer2", Amt = 234 });
CustomersList.Add(new Customer() { ID = 3, Name = "TestCustomer3", Amt = 324 });
ViewBag.CustList = CustomersList;
return View(new CustomerList() { Customers = CustomersList });
}
[HttpPost]
public void DisplayCustomer(List<int> selectedIds)
{
// do something with the id list
}
View
#model MvcApplication2.Models.CustomerList
#using (Html.BeginForm(#Model.SelectedIDs))
{
#Html.ListBoxFor(m => m.SelectedIDs, new MultiSelectList(#Model.Customers, "ID", "Name", #Model.SelectedIDs))
<input type="submit" value="save" />
}
As mentioned here
Hope that works!!!

Resources