I have problem in my code. I\'m using the registration form which comes with MVC5 , I added a field \"Role\" as a Dropdownlist to assign a role to the user while creating a
You cannot bind a <select>
element to a complex object (which is what Role
is) and when you submit the form, ModelState
is invalid (your trying to bind the selected Name
property of RolesList
to typeof IdentityRole
). You then return the view, but have not repopulated the RolesList
property, so its null
(hence the error).
View models should not contain data models, and you view model should be
public class RegisterViewModel
{
....
[Required(ErrorMessage = "Please select a role")]
public string Role { get; set; }
public IEnumerable<SelectListItem> RoleList { get; set; }
}
and in the GET method
var roles = _context.Roles.Select(r => r.Name);
var viewModel = new RegisterViewModel
{
RolesList = new SelectList(roles)
};
return View(viewModel);
and in the view
@Html.LabelFor(m => m.Role, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.DropDownListFor(m => m.Role, Model.RolesList, "Select Role", new { @class = "form-control" })
</div>
this will solve the invalid ModelState
issue relating to Role
, but if you do need to return the view because of other ModelState
issues, then you must first repopulate the collection before returning the view
if (!ModelState.IsValid)
{
var roles = _context.Roles.Select(r => r.Name);
model.RolesList = new SelectList(roles);
return View(model);
}
.... // save and redirect
okay, i got the same problem, and none of the solution on this page helped me unless i figured it on my own.
while posting the form ,
when we use
If( ModelState.IsValid())
{
//post here
}
return view (model);
actually we are not initializing the items for dropdown list in the POST method. thats why this nullaugumentexception was thrown . we just need to initialize the list of items in the model inside the post method also as we did while calling the form, so that it can initialize the list if the model during the posting is not valid , and then put the model in return statement.
The solution that worked for me in MVC5 for editing an existing user
Model (part of)
public IEnumerable<string> Roles { get; set; }
View
@Html.DropDownListFor(model => model.UserRole, new SelectList(Model.Roles, Model.UserRole), new { @class = "form-control" })
Controller (Get)
var model = new EditUserViewModel()
{
UserName = user.UserName,
Email = user.Email,
IsEnabled = user.IsEnabled,
Id = user.Id,
PhoneNumber = user.PhoneNumber,
UserRole = userRoleName,
// a list of all roles
Roles = from r in RoleManager.Roles orderby r.Name select r.Name
};
Personally I would create the select list in your controller and pass it to the model, writing something like
var RolesList = new List<SelectListItem>
{
new SelectListItem { Value = string.Empty, Text = "Please select a Role..." }
};
RolesList.AddRange(roles.Select(t => new SelectListItem
{
Text = t.role,
Value = t.Id.ToString()
}));`
and then add this to your model, in your model you can then use
@Html.DropDownListFor(m => m.Role, Model.RoleList, new { @class = "form-control" })
This method/syntax works for me I am not sure if It is 'best practice' though
Make sure you add the list to the model before returning the view, or you will get this error. Example:
cshtml:
@model DelegatePortal.ViewModels.ImpersonateVendorViewModel
@using (Html.BeginForm("ImpersonateVendor", "Admin", FormMethod.Post))
{
@Html.DropDownListFor(model => model.Id, new SelectList(Model.Vendors, "Id", "Name"), "Choose a vendor", new { @class = "form-control form-control-sm " })
}
controller:
// GET: /Admin/ImpersonateVendor
public ActionResult ImpersonateVendor()
{
ImpersonateVendorViewModel model = new ImpersonateVendorViewModel();
var vendors = (from c in db.Vendors
select c).ToList();
model.Vendors = vendors; --> add list here
return View(model);
}