I'm fairly new to ASP.NET MVC and while I'm aware that ignorance isn't an excuse I couldn't find any helpful(to me) answers from similar questions.
Issue:
My ModelState is invalid because I've bound the id of the RoleDTO model( a model in a partial view which is in begincollectionitem ) to the dropdownlist using Html.DropDownListFor( r => r.Id, ((IEnumerable<SelectListItem>)ViewBag.PossibleRoles)). Because of that the Name property of the RoleDTO model is always empty and ModelState validation fails. How do I handle this situation properly?
Details:
I've got User and Role models with N-N relationship, and their DTOs.
I've got a UserController wherein I've got create/edit actions. Here's how Edit GET and POST actions look:
[HttpGet]
public ActionResult Edit(int id)
{
var model = userRepository.Get(id);
LoadPossibleRoles();
return View(model);
}
[HttpPost]
public ActionResult Edit(UserDTO model)
{
if (ModelState.IsValid)
{
userRepository.InsertOrUpdate(model);
userRepository.Save();
return RedirectToAction("Index");
}
else
{
LoadPossibleRoles();
return View(model);
}
}
What LoadPossibleRoles() does is retrieve all roles from the repository, convert them to SelectListItems and puts them in the viewbag(I should probably create a viewmodel or something but I'm unsure how to create/use one in my situation). It looks like this:
private void LoadPossibleRoles()
{
var availableRoles = roleRepository.GetAll();
var selectItems = new List<SelectListItem>();
foreach (var role in availableRoles)
{
var listItem = new SelectListItem();
listItem.Value = role.Id.ToString();
listItem.Text = role.Name;
selectItems.Add(listItem);
}
ViewBag.PossibleRoles = selectItems;
}
I've got a _CreateOrEdit partial view whose model is UserDTO In which I've got _SingleRoleEdit partial for each added role. It looks like this:
Add a role
#foreach( var r in Model.Roles)
{
#Html.Partial("_SingleRoleEdit", r)
}
The insertNewRole() js function is an ajax call which calls "CreateNewRole" method in the UserController. The method looks like this:
public PartialViewResult CreateNewRole()
{
LoadPossibleRoles();
return PartialView("_SingleRoleEdit", new RoleDTO());
}
And finally in _SingleRoleEdit partial I generate the dropdown list for all available roles:
#using (Html.BeginCollectionItem("Roles"))
{
<table class="editor-table">
<tr>
<th>Role</th>
<td>#Html.DropDownListFor( r => r.Id, ((IEnumerable<SelectListItem>)ViewBag.PossibleRoles))</td>
<td>X</td>
</tr>
</table>
}
Presentationally this works as it should. I can add/remove dropdownlists which actually do show all available roles. The 'issue' is the way the data binding currently works. DropDownListFor binds the selected value in the dropdownlist to the models Id, however there is currently nothing binding the name to the text of the dropdownlist and that's why my ModelState is always invalid on Post. I'm aware that binding the text of the selection in the dropdownlist to the model is a bad idea. So I'm looking for a proper way to handle this situation.
Related
my view:
#using (Html.BeginForm("Index", "Person"))
{
#Html.DropDownList("Departments",ViewData["Departments"] as SelectList)
<button type="submit">Select</button>
}
my DepartmentController:
public ActionResult Index()
{
ViewData["Departments"] = new SelectList(db.Departments, "ID", "Name");
return View();
}
my PersonController
public ActionResult Index(string id = null)
{
if (id != null)
{
//Return list of persons with department id
}
return View();
}
My problem:
When I select a department from the DropDown and press the button, it redirects fine, but the id is not passed.
What am I missing? I'm guessing it has to do with how i fill the DropDownList?
Anyway, as always, thanks in advance
The name attribute of your dropdown is not "id" so the MVC model binder can not bind it.
Add html attribute new {#Name ='id'} to your DropDown definition and it should work.
I also would suggest that your view receive a model - in this case model binding will be much easier, and you could use DropDownFor helper.
Using model also allows you to avoid using ViewData and ViewBag containers that are not recommended because they are not strongly typed so if by mistake you write ViewData["Departnents"] in your View you won't get a compilation error because of the typo, but clearly it won't work.
As an opposite you can define a model
public class Person
{
public SelectList Departments {get; set;}
public int SelectedDepatrmentId {get; set;}
//Other person properties come here
}
In your View the only thing you should do to make it work is:
#model path to your Person class
#Html.DropDownListFor(model => model.SelectedDepatrmentId, Model.Departments)
The problem in your case that mvc model binding is done with name attribute and #Html.DropDownList("Departments"... will render html with dropdown having name 'Departments' so either try my first answer or change #Html.DropDownList("Departments"... as shown in my second answer.
Try This :
public ActionResult Index(string Departments) // <------ Use 'Departments' here instead of 'id'
{
.....
return View();
}
OR change dropdownlist as :
#Html.DropDownList("id",ViewData["Departments"] as SelectList)
I know it was silly to ask this question but I'm not able to figure it out and I need your help guys.
First of all I am new to MVC. In my Project I am using a dropdownlistFor helper for displaying a list of names available for a particular Id. I did that and it is displaying names for an id.
Now while posting the form I am getting a Null Reference Exception for property used in the dropdownlist.
Here is my property in model which is a list of names.
In my controller in the [HttpGet] I did this which calls a function and returns a list of names for that Id.
Now the list of names is being displyed while form loading. And my view is as
When I am submitting the form I am getting a Null Reference Exception because in new SelectList(Model.InterviewerName) the Model is NULL.
Is there anyway to get me out of this issue.
I think you should update your viewmodel like this:
public class InterviewViewModel
{
public List<SelectListItem> Interviewers { set;get;}
public int SelectedInterviewerID { set;get;}
//Other properties relevant to the view as needed
}
And in your GET action set the Interviewers collection property:
public ActionResult Interview()
{
var vm=new InterviewViewModel();
vm.Interviewers =GetInterViewrsFromSomewhere();
return View(vm);
}
public List<SelectListItem> GetInterViewrsFromSomewhere()
{
var list=new List<SelectListItem>();
//Items hard coded for demo. you can read from your db and fill here
list.Add(new SelectListItem { Value="1", Text="AA"});
list.Add(new SelectListItem { Value="2", Text="BB"});
return list;
}
And in your view which is strongly typed to InterviewViewModel
#model InterviewViewModel
#using(Html.Beginform())
{
<p>Select interviewer :
#Html.DropdownlistFor(x=>x.SelectedInterviewerID,Model.Interviewers,"Select")
<input type="submit" />
}
So when the form is posted, The selected interviewers id will be available in the SelectedInterviewerID property:
[HttpPost]
public ActionResult Interview(InterviewViewModel model)
{
if(ModelState.IsValid)
{
//check for model.SelectedIntervieweID value
//to do :Save and redirect
}
//Reload the dropdown data again before returning to view
vm.Interviewers=GetInterViewrsFromSomewhere();
return View(vm);
}
In the HttpPost action method, if you are returning the viewmodel back to the view, you need to refill the dropdown content because HTTP is stateless and it won't keep the dropdown content between the requests (Webforms does this using Viewstate and here in MVC we don't have that).
I think i spotted the problem, you need to do the following two things to resolve,
1) change the model property public IList<string> InterviewerName to public string InterviewerName
2) use ViewBag to take the selectlist values to the View.
Let me know if it helps.
i want to show a dropdownList on a page using Entity Framework in my MVC app, but i am just stuck here to do this using using HTML Helper. so if anyone having knowledge of entity framework, help me...
my dataContext partial class is Entities, in which an entity named MemberInfo have some fields including MemberID & MemberName, so my dropdownList should show the field MemberName & behind this the value should be MemberID,
the code i tried yet--->
#Html.DropDownListFor(Model => Model.MemberID, MemberInfo)
in controller i am returning the Model--->
var MemberNameList = FinanceDBContext.MemberInfoes.Select(x => new { x.MemberID, x.Name });
return View(MemberNameList);
but its not working (errors).
You need to pass in all of your objects as the "model". Best practice is to use a ViewModel which will contain the list of data and a property to store the selected item.
ViewModel
public class MyViewModel
{
// The drop-down list and variable to get selection
public List<Member> Members { get; set; }
public int SelectedMemberId { get; set; }
}
Controller
[HttpGet]
public ActionResult Index()
{
var viewModel = new MyViewModel();
viewModel.Members = FinanceDBContext.MemberInfoes.ToList();
return View(viewModel);
}
[HttpPost]
public ActionResult Index(MyViewModel viewModel)
{
string debug = string.Format("You selected member: {0}", viewModel.SelectedMemberId);
return View(viewModel);
}
Finally, in your view (these lines need to be inside a BeginForm { ... } and ensure your View is strongly typed to MyViewModel
#Html.DropDownList("SelectedMemberId", new SelectList(Model.Members, "MemberID", "Name"))
<input type="submit" value="Save etc..." />
In this example you could put a break-point on the HttpPost action and check the debug string to check the correct Member is returned and proceed as required.
I have one problem.
This is short example.
This is model.
public class MyModel
{
string Title{get;set;}
}
In view I write
#Html.TextBoxFor(model => model.Title)
This is controller.
public ActionResult EditNews(int id)
{
var model = new MyModel;
MyModel.Title = "SomeTitle"
return View("News/Edit", model);
}
//for post
[HttpPost]
public ActionResult EditNews(MyModel model)
{
//There is problem.When I do postback and
// change Title in this place,Title doesn't change in view textbox
//Only when I reload page it change.
model.Title = "NEWTITLE"
return View("News/Edit", model);
}
It won't change because by default (many think this is a bug) MVC will ignore the changes you make to the model in a HttpPost when you're returning the same View. Instead, it looks in the ModelState for the value that was originally served to the view.
In order to prevent this, you need to clear the ModelState, which you can do at the top of your HttpPost by doing:
ModelState.Clear();
This question is related to another I ask recently, it can be found here for some background information.
Here is the code in the Edit ActionResult:
public virtual ActionResult Edit(int id)
{
///Set data for DropDownLists.
ViewData["MethodList"] = tr.ListMethods();
ViewData["GenderList"] = tr.ListGenders();
ViewData["FocusAreaList"] = tr.ListFocusAreas();
ViewData["SiteList"] = tr.ListSites();
ViewData["TypeList"] = tr.ListTalkbackTypes();
ViewData["CategoryList"] = tr.ListCategories();
return View(tr.GetTalkback(id));
}
I add lists to the ViewData to use in the dropdownlists, these are all IEnumerable and are all returning values.
GetTalkback() returns an Entity framework object of type Talkback which is generated from the Talkback table.
The DropDownListFor code is:
<%: Html.DropDownListFor(model=>model.method_id,new SelectList(ViewData["MethodList"] as IEnumerable<SelectListItem>,"Value","Text",Model.method_id)) %>
The record I am viewing has values in all fields. When I click submit on the View, I get an Object reference not set to an instance of an object. error on the above line.
There are a number of standard fields in the form prior to this, so the error is only occurring on dropdown lists, and it is occurring on all of them.
Any ideas? This is my first foray in to MVC, C#, and Entity so I am completely lost!
If you have [HttpPost] method like that
[HttpPost]
public ActionResult Edit(Talkback model)
{
//Do something with model
return View(model);
}
You have to fill ViewData again. If you don't do it, you'll have Object reference not set to an instance of an object errors.
The best thing to do would be to follow POST-REDIRECT-GET patter and do it like that:
[HttpPost]
public ActionResult Edit(Talkback model)
{
//Do something with model
return RedirectToAction("Edit", new { id = model.id });
}
You'll have ViewData filled again by [HttpGet] method.