I am trying to understand the best way of implementing a DropDownList in ASP.NET MVC 2 using the DropDownListFor helper. This is a multi-part question.
First, what is the best way to pass the list data to the view?
Pass the list in your model with a SelectList property that contains the data
Pass the list in via ViewData
How do I get a blank value in the DropDownList? Should I build it into the SelectList when I am creating it or is there some other means to tell the helper to auto create an empty value?
Lastly, if for some reason there is a server side error and I need to redisplay the screen with the DropDownList, do I need to fetch the list values again to pass into the view model? This data is not maintained between posts (at least not when I pass it via my view model) so I was going to just fetch it again (it's cached). Am I going about this correctly?
Your best bet is to create a SelectList in your Controller - use my extension method here:
http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc
Pop that into ViewData using the same key as your property name:
ViewData["statusid"]=MySelectList
Then just use Html.DropDownFor(x=>x.StatusID) and you're all set.
Answering in parts:
The best way IMHO is to pass the list in the ViewModel like this:
public SelectList Colors
{
get
{
// Getting a list of Colors from the database for example...
List<Color> colors = GetColors().ToList();
// Returning a SelectList to be used on the View side
return new SelectList(colors, "Value", "Name");
}
}
To get a blank or default option like ( -- Pick a color -- ), you can do this on the view side:
#Html.DropDownListFor(m => m.Color, Model.Colors, "-- Pick a color --")
You'll have to fetch/populate the list again if it's part of the ViewModel.
Take a look at the following blog post. It can give you some tips:
Drop-down Lists and ASP.NET MVC
You could do something like:
<%= Html.DropDownListFor((x => x.ListItems), Model.ListItems, "")%>
or
<%= Html.DropDownList("ListItems", Model.ListItems, "")%>
The last param 'optionLabel' makes a blank list item
In this case, you can see ListItems is a property of the model.
I have made the view strongly typed to the model also.
(You know this already!)
Pass the list in your model with a SelectList property that contains the data
Yes, add it when you build the SelectList. (If you build the list using LINQ, Union might come in handy.)
Yes do do, and yes you are.
I find it more intuitive to work with a sequence of SelectListItems (rather than a SelectList).
For example, this would create an IEnumerable<SelectListItem> from a sequence of customer objects that you can pass to the Html.DropDownListFor(...) helper. The 'Selected' property will optionally set the default item in the dropdown list.
var customers = ... // Get Customers
var items = customers.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCustomerId),
Text = c.Email,
Value = c.Id.ToString()
});
Related
In the view I have a Model that consists of 2 Lists. The Model.Ingredients is full of Ingredient objects, the second one UsedIngredients is initialized but empty.
If the user clicks on an ingredient i want to add it to the empty list, because i want to pass that list to a partial view.
#foreach (var item in Model.Ingredients)
{
if (item.Type == "spirit")
{
<a onclick="#Model.UsedIngredients.Add(item)">#item.Name</a>
}
}
If I Try this I get an error:
Cannot implicitly convert type 'void' to 'object'.
I have the list initialized correctly i think:
List<Models.Extended.Ingredient> list = new List<Models.Extended.Ingredient>();
creator.UsedIngredients = list;
Ok turns out I’m kinda dumb because why would it work? We cannot run server side code onclick action like this. So I have to use session variables and httpPost method.
I am trying to pass a 2nd List of objects from the Controller to the
View via the ViewBag
Here is the line from my controller code.
ViewBag.FeaturedProductList = await Service.SendAsync(new ProductQuery());
The return object is the following
public class FeaturedProductListDto : IDto
{
public IEnumerable<FeaturedProductDto> Contents { get; set; }
}
In the View, I need to do a linq to select from the
ViewBag.ViewBag.FeaturedProductList in the following line.
#foreach (var productGroup in ViewBag.FeaturedProductList.Select((e,
i) => new {Product = e, Grouping = (i/3)}).GroupBy(e => e.Grouping))
{
}
I need to group the number of items from the list in sets of 3 but the
Select is throwing an error as the following
Cannot use a lambda expression as an argument to a dynamically
dispatched operation without first casting it to a delegate or
expression tree type.
I used the same code for my other List which I passed in as the Model
and it works.
This line works. #foreach (var productGroup in Model.Select((e, i) =>
new { Product = e, Grouping = (i / 4) }).GroupBy(e => e.Grouping))
Do I need to recast the ViewBag.FeaturedProductList? What is the fix
for this? Any help is greatly appreciated. Thanks.
ViewBag is a dynamic dictionary. Items in this dictionary are of type dynamic. You cannot use LINQ methods on that.
You need to cast ViewBag.FeaturedProductList to FeaturedProductListDto type and use the Contents property which is a collection type on which we can apply LINQ extension methods.
#foreach (var item in ((FeaturedProductListDto) ViewBag.FeaturedProductList).Contents
.Select(//your existing select code goes here))
{
}
I am not quite sure what you are trying to do with the GroupBy inside your Select. My personal preference is doing all such things in the action method/another layer which provides data to the action method and keep the razor markup with minimal C# code, and with more HTML markup for the page. :)
Currently I have this AcceptedProposals view, which is meant to show the details of a list of proposals, which are coming from the Entity Framework. Here's the code I have in my controller:
public ActionResult AcceptedProposals()
{
var proposals = db.Proposals.Where(p => p.CollegeFundDecision == true);
return View(proposals);
}
In my view I have the following line, but it's giving me this error:
System.InvalidOperationException: The model item passed into the
dictionary is of type
'System.Data.Objects.ObjectQuery`1[URGLibrary.Proposal]', but this
dictionary requires a model item of type 'URGLibrary.Proposal'.
#(Html.Telerik().Grid<Proposal>((IEnumerable<Proposal>)Model))
Any idea how I would be able to this this grid to show up properly? And further more, once I get this working, if I wanted to make it so that there's a dropdown of years to choose what year of proposals I'm looking at, would I be putting this grid code into a partial view?
Thanks for the help!
You havent enumerated your set prior to passing it to the view. Its good practice to use .ToArray or .ToList to pre-enumerate your set in the controller so that you cant accidentally add bits to it in the view. This means your action should look like this:
public ActionResult AcceptedProposals()
{
var proposals = db.Proposals.Where(p => p.CollegeFundDecision == true).ToArray();
return View(proposals);
}
This relates to the ObjectQuery part of the error (a collection which hasnt yet been finalised and retrieved from the database yet).
Try this and let me know if it resolves the issue or gives you a simpler error message.
I have a very strange problem. I want to change selected value of the dropdownlist after form submition. I know that HtmlHelper is retrieving the ModelState value, which is filled with the posted data. But I have a redirect from my POST action to GET action! However my ddl is populated with value submitted during the post. I've also added this code to my post action: ModelState.Clear(), but this hasn't helped me neither.
I've added another ddl to my form just for debugging;
#Html.DropDownList("asd" + Guid.NewGuid(), Model.Voting.Result.ToSelectList())
It always appears with value provided by the server code. But the target ddl
#Html.DropDownListFor(x => x.Voting.Result, Model.Voting.Result.ToSelectList())
always has a value posted by user.
How can I populate the target ddl?
You have to pass the initial value to it like
#Html.DropDownListFor(x => x.Voting.Result,
new SelectList(Model.Voting.Result, "Id", "Name", /*initial value*/))
Use this constructor of SelectList class
Edit
initially I have put the argument on wrong method
When a controller renders a view based on a model you can get the properties from the ViewData collection using the indexer (ie. ViewData["Property"]). However, I have a shared user control that I tried to call using the following:
return View("Message", new { DisplayMessage = "This is a test" });
and on my Message control I had this:
<%= ViewData["DisplayMessage"] %>
I would think this would render the DisplayMessage correctly, however, null is being returned. After a heavy dose of tinkering around, I finally created a "MessageData" class in order to strongly type my user control:
public class MessageControl : ViewUserControl<MessageData>
and now this call works:
return View("Message", new MessageData() { DisplayMessage = "This is a test" });
and can be displayed like this:
<%= ViewData.Model.DisplayMessage %>
Why wouldn't the DisplayMessage property be added to the ViewData (ie. ViewData["DisplayMessage"]) collection without strong typing the user control? Is this by design? Wouldn't it make sense that ViewData would contain a key for "DisplayMessage"?
The method
ViewData.Eval("DisplayMessage")
should work for you.
Of course after I create this question I immediately find the answer after a few more searches on Google
http://forums.asp.net/t/1197059.aspx
Apparently this happens because of the wrapper class. Even so, it seems like any property passed should get added to the ViewData collection by default.
I really need to stop answering my own questions :(