SelectList method MVC - asp.net-mvc

Problem Description:
I am using ASP.NET MVC and I have the following method in my Controller Class. This method uses SelectList method to select a list of items from the database. These items will then be passed to the view to be displayed in a drop down list.
public ActionResult Edit(int id)
{
Album album = db.Albums.Find(id);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
My Understanding of the selectMethod:
I know that the first param takes a list of items.
The third param is the property to be displayed.In this case, we will be displaying the name property of the Artists.
Based on my research, I found out that the 4th param is the default value that will be displayed on the dropdown list.
My Question:
1) I would like someone to help me understand about the second param.
2)Since we are displaying names of the artists, how can we display a default artist name in the fourth param using Artist ID?
I hope you guys understood my questions. I would be happy to clarify them to you if you need me to do so.

The second parameter is the name of the "value" property or field on each element in the first parameter. Since two artists could conceivably have the same, it's generally better to use an ID column (as in the example) so that you know specifically which item was selected.
In the example, you are providing the artist of the currently-viewed album as the default to appear. Assuming that db.Artists includes an artist whose ArtistId property matches the given value, the rendered HTML will produce a select list where that artist is selected.
Did that answer your questions?

Per the documentation:
The first parameter is an IEnumerable of objects from which to construct the list.
The second parameter is the name of the object property (for each object in the list) to be used as the value attribute of each rendered HTML <option> element.
The third parameter is the name of the object property (for each object in the list) to be used as the text attribute of each rendered HTML <option>.
The fourth parameter is the default selected value (which indicates the element of the list that will be rendered with the selected attribute).

Related

MVC4 EF cannot get saved value to display in dropdown on page load

I have never asked a question on StackOverflow before, and never wanted to, but I am desperate, so here we go: I cannot get a saved value to show up as the default value/display in a dropdown.
I set up the list in my controller:
public ActionResult Index()
{
//User Dropdown List
var users = Roles.GetUsersInRole("Manager");
SelectList list = new SelectList(users);
ViewBag.Users = list;
return View();
}
Then in the view an admin can then select one of these users and save it to my database via EF:
#Html.DropDownList("Users", ViewBag.Users as SelectList, "--Select Manager--")
This all works great, however, when you edit this entry, I want the dropdown list to show the current saved manager, not the first name in the list. I was hoping on my edit action that I could pull the current manager out of the database and pass it back into the dropdown as the default selected item, but no go:
public ActionResult Edit(int id = 0)
{
var theOwner = (from v in _db.Location where v.LocationID == id select v.Owner).FirstOrDefault();
var users = Roles.GetUsersInRole("Manager");
SelectList list = new SelectList(users, theOwner);
ViewBag.Users = list;
From all the examples I have read over the last 2 weeks, everyone has had 3 different values to work within their dropdowns, making it possible to use all the overloads in the SelectList method. However, my problem is that I just have this string list with only one item in it, so I can't utilize the overloads as I want.
So does anyone have an idea on how I can get this to work? Thanks a lot in advance for your time on this!
I'm pretty sure that if you modify the second parameter on the line where you create your SelectList, it should work -- it does for me.
Here is what I think the trouble is: Currently you are specifying the second parameter as 'theOwner', which is an object reference from the earlier Linq statement. But the SelectList contains a bunch of strings (the UserNames of the users which match the specified rolename). As a result, the SelectList doesn't 'know' how to match what you specified as the SelectedItem to something in the list of strings it contains.
But if you refine that second parameter so it specifies the USERNAME of the Owner that you just looked up, it should work. However I do not know what the correct property name is from your Location table. If the field you are currently selecting (v.Owner) contains the UserName itself rather than some Key then the syntax would be:
SelectList list = new SelectList(users, theOwner.Owner);
If that column actually contains a key for the User like an int or a Guid then you will have query for the UserName using the key, but the nature of the fix is the same.
Hope that helps.
A quick workaround is not to use #Html.DropDownList but plain html code.
As an example for your case, use the following html code in your View instead of Html.DropDownList helper:
<!-- NOTE: the ID and name attributes of "select" tag should be the same as
the name of the corresponding property in your Model in order for ASP.NET MVC
to edit your Model correctly! -->
<select id="User" name="User">
#foreach (var user in (SelectList)ViewBag.Users)
{
if (user == ViewBag.TheOwner)
{
<option value="#user" text="#user" selected = "selected" />
}
else
{
<option value="#user" text="#user" />
}
}
</select>
Also , for this to work you need to add one more line to your Edit method:
ViewBag.TheOwner = theOwner;
Another solution is also possible using #Html.DropDownListFor() however you haven't shown your model so I can't tell you what exactly to use. When DropDownListFor is used, ASP.NET MVC will select an option automatically based on the value in your model.

String[] vs Integer[] in Struts2 action attribute

I am new t struts2. i built one page, that shows list of employees. I can seach the employes based on his name by applying filter criteria and click on find button. At the same time, i provided check box in the left side for each employee name for delete operation. for all checkboxes, i gave Integer[] attribute name which is declared in Custom Actionclass.deleteaction is working fine. But when i click Find button the action is not getting submitted. Then i changed Integer[] to String[] both functions are working fine. What will be the problem? is it something like, attributes should only be String type.
The cause of your problem is that the Struts2 checkbox sets a boolean property on the action class:
Struts 2 Form Tags: Checkbox
When you defined the checkboxes as an Integers, the framework couldn't covert the boolean to an Integer. However it was able to convert the boolean to Strings. If you check the results in your action class, you should see the String[] populated with "true" and "false".
In general Struts2 is pretty good at converting submitted form data to whatever object type you want. Check out the docs on type conversion for more info.

Persisting Selected Value in MVC DropDownLists

I'm new to MVC, but I've been all over this, read all the documentation and all the questions and all the blog posts I can find, and all I'm doing is getting completely wrapped around the axle.
I'm trying to make a "create" Action and View. My data entry is relatively straight forward, and common: I have a drop down list and a text box. In my case, I'm creating a user contact channel, and the drop down box chooses between email and textmsg, and the text box then enters the relevant contact information, either a well formed email address, or a mobile phone number.
Here's a (slightly simplified form of) my View page:
<tr>
<td><%= Html.DropDownList("ChannelDescription", Model.ChannelDescription, "Select a Channel", new { id = "ChannelDDL", onchange="ChannelDDLChanged()" })%>
<br />
<%= Html.ValidationMessage("ChannelDescription", "Please Select a Channel") %>
</td>
<td>
<%= Html.TextBox("SubscriberNotificationAddr") %> <br />
<%= Html.ValidationMessage("SubscriberNotificationAddr", "Please enter a contact address or number") %>
</td>
</tr>
I'm using a strongly typed ViewData model, rather than using the ViewDataDictionary. The ChannelDescription element is a SelectList, which is initialized with the list of choices and no selection.
The initial display of the form, the data entry into the form, and the extraction of the data from the form by the controller goes fine.
My problem is if the data contains an error, such as a mal-formed email address or cell phone number, and I have to return to the view, I have not been successful in getting the drop down list selection redisplayed. The ChannelDescription element is recreated in the controller with the user's choice as the selected item. I have set breakpoints on that line of the View, and verified that the selected element of the list of items has the Selected property set to true, but it still displays the default "Select a Channel".
This seems like it would be a very common situation, and shouldn't be this hard. What am I doing wrong?
FYI, this is with MVC 1.0 (Release), Windows 7, and VS 2008, running under Firefox 3.5.2.
After viewing the answer above, I wanted to check it out, because all the examples I had seen had, indeed, used ViewDataDictionary, rather than a strongly typed ViewDataModel.
So I did some experiments. I constructed a very simple view that used a plain ViewDataDictionary, and passed values in by named keys. It persisted the selected item just fine. Then I cut and pasted that View (and controller) to another one, changing only what was necessary to switch to a strongly typed ViewData Model. Lo, and behold, it also persisted the selected item.
So what else was different between my simple test and my application? In my test, I had used simply "Html.DropDownList("name", "optionLabel")". However, in my application, I had needed to add HTML attributes, and the only overloads available that included HtmlAttributes also include the select List.
It turns out that the DropDownList overload with a select list parameter is broke! Looking at the downloaded MVC source code, when DropDownList is called with just a name, or a name and an optionLabel, it ends up retrieving the target select list from the ViewData, and then invoking the private SelectInternal method by the following call:
return SelectInternal(htmlHelper, optionLabel, name, selectList, true /* usedViewData */, false /* allowMultiple */, (IDictionary<string, object>)null /* htmlAttributes */);
However, if it's called with a selectList parameter, it ends up with the following:
return SelectInternal(htmlHelper, optionLabel, name, selectList, false /* usedViewData */, false /* allowMultiple */, htmlAttributes);
The difference is that in the first one (which will work correctly) the "usedViewData" parameter is true, while in the second one, it is false. Which is actually okay, but exposes an internal defect in the SelectInternal routine.
If usedViewData is false, it gets a object variable "defaultValue" from the ViewData model.
However, defaultValue is used as though it is either a string or an array of strings, when, in fact what is returned from the ViewData is a SelectList. (IEnumerable<SelectListItem>).
If usedViewData is true, then defaultValue will be either null or a string.
Then if defaultValue is not null, it ends up going into a block of code which contains this:
foreach (SelectListItem item in selectList) {
item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
newSelectList.Add(item);
selectList is the original selectList that was passed in, so the item is a SelectListItem (string Text, string Value, and bool Selected). But selectedValues was derived from the defaultValue, and becomes a List of SelectLists, not a List of strings. So for each of the items, it's setting the Selected flag based on whether the selectedValues list "Contains" the item.Value. Well, a List of SelectLists is never going to "Contain" a string, so the item.Selected never gets set. (Correction: actually, after more tracing with the debugger, I found that selectedValues is derived from the defaultValue by a "ToString()" call. So it actually is a list of strings, but instead of containing the values that we want, it contains "System.Web.Mvc.SelectList" - the result of applying "ToString()" to complex object like a SelectList. The result is still the same - we're not going to find the value we're looking for in that list.)
It then substitutes the newly constructed "newSelectList" for the original "selectList", and proceeds to build the HTML from it.
As cagdas (I apologize for butchering your name, but I don't know how to make those characters on my US Keyboard) said above, I think I'll have to build my own method to use in place of the DropDownList HtmlHelper. I guess since this release 1 and Release2 is in Beta 2, we can't really expect any bug fixes unless we do it ourselves right?
BTW, if you've followed me this far, this code is in src\SystemWebMvc\Mvc\Html\SelectExtensions.cs, at around line 116-136
I had some discussions with Brad Wilson, from the MVC team, and he explained to me that I was misunderstanding how the DropDownList helper method should be used (a misunderstanding that I think might be fairly common, from what I've read).
Basically, EITHER give it the SelectList in the named parameter of the ViewModel, and let it build the drop down list from that with the appropriate items selected, OR give it the SelectList as a separate parameter and let the named parameter of the ViewModel be just the value strings for the selected item(s). If you give it a SelectList parameter, then it expects the named value to be a string or list of strings, NOT a SelectList.
So, now your ViewModel ends up having two elements for one conceptual item in the view (the dropdown list). Thus, you might have a model that has
string SelectedValue {get; set;}
SelectList DropDownElements { get; set;}
Then you can pre-populate the DropDownElements with the choices, but in your model view binding, you just need to deal with SelectedValue element. It seems to work pretty well for me when I do it that way.
Yes, I too had so many problems getting DropDownList to respect the selected item I've given to it.
Please check my answer in this question. As far as I can remember, that was the only way I could get it to work. By passing the list via ViewData.
FYI, I stopped using that HtmlHelper method. I'm now simply outputting the <select> and <option> tags myself with a loop and setting the selected property of the option tag by checking it myself.

Add empty value to a DropDownList in ASP.net MVC

I'm building a data entry interface and have successfully bound the columns that have reference tables for their data using DropDownList so the user selects from the pre-configured values.
My problem now is that I don't want the first value to be selected by default, I need to force the user to select a value from the list to avoid errors where they didn't pick that field and by default a value was assigned.
Is there a more elegant way of doing this than to add code to include an empty value at the top of the list after I get it from the database and before i pass it to the SelectList constructor in my controller class?
The Html helper function takes a 'first empty value' parameter as the third argument.
<%=Html.DropDownList("name",dataSource,"-please select item-")%>
You can also use this way:
dropdownlist.DataTextField = ds.Tables[0].Columns[0].Caption;
dropdownlist.DataValueField = ds.Tables[0].Columns[1].Caption;
dropdownlist.DataSource = ds;
dropdownlist.DataBind();
dropdownlist.Items.Insert(0, new ListItem("Select ...", string.Empty));

How to pass an unpersisted modified object from view back to controller without a form?

Short: how does modelbinding pass objects from view to controller?
Long:
First, based on the parameters given by the user through a search form, some objects are retrieved from the database.
These objects are given meta data that are visible(but not defining) to the customer (e.g: naming and pricing of the objects differ from region to region).
Later on in the site, the user can click links that should show details of these objects.
Because these meta data are important for displaying, but not defining, I need to get the previously altered object back in the controller.
When I use the default asp.net mvc modelbinding, the .ToString() method is used. This off course doesn't return a relevant string for recreating the complete object.
I would have figured the ISerializable interface would be involved, but this is not so.
How should I go about to get the desired effect? I can't imagine I'm the first one to be faced with this question, so I guess I'm missing something somewhere...
The default model binding takes form parameters by name and matches them up with the properties of the type specified in the argument list. For example, your model has properties "Price" and "Name", then the form would need to contain inputs with ids/names "Price" and "Name" (I suspect it does a case insensitive match). The binder uses reflection to convert the form values associated with these keys into the appropriate type and assigns it to the properties of a newly created object of the type specified by the parameter (again derived by reflection).
You can actually look at (and download) the source for this at http://www.codeplex.com/aspnet, although you'll have to drill down into the MVC source from there. I'd give a link to the DefaultModelBinder source, but the way they are constructed, I believe the link changes as revisions are introduced.
So, to answer your question, you need to have parameters (could be hidden) on your form that correspond to the properties of the object that you want to recreate. When you POST the form (in the view) to the controller, the binder should reconstitute an object of the specified type using the form parameters. If you need to do translation from the values in the form parameter to the object properties, you'll probably need to implement your own custom model binder.
[EDIT] In response to your second post:
Let's say that we want to have a link back to an action that uses a customized object. We can store the customized object in TempData (or the Session if we need it to last more through more than one postback) with a particular key. We can then construct the action link and provide the key of the object as value to the ActionLink in an anonymous class. This will pass back the key as a Request parameter. In our action we can use the key from this parameter to retrieve the object from TempData.
<%= Html.ActionLink( ViewData["CustomObject1",
"Select",
new { TempDataKey = ViewData["CustomObject1_Key"] }
) %>
public ActionResult Select()
{
Entity custObj = null;
string objKey = Request.Params["TempDataKey"];
if (!string.IsNullOrEmpty(objKey))
{
custObj = (Entity)TempData[objKey];
}
... continue processing
}
#tvanfosson
Thanks for your explanation, but what about links? (no forms involved)
Currently the Html.ActionLink(c=>c.Action(parameter), "label") takes objects as parameter. These have to be translated into URL parts. For this, MVC ALWAYS goes to the .ToString() method. I don't want to serialize my object in the ToString method.
Shouldn't I be able to somehow help the framework serialize my object? Say through the ISerialize interface or something?

Resources