Why is the DropDown values Null in the POST action? - asp.net-mvc

Below is the View:
<%# Page Title="" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Index>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Contact
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm())
{%>
<%=Html.DropDownListFor(x => x.SelectedFavColor, Model.DropDownItems)%>
<%= Html.ValidationMessageFor(x=> x.SelectedFavColor) %>
<input type="submit" value="submit" />
<%} %>
</asp:Content>
Below mentioned is the Model:
namespace MvcApplication1.Models
{
public class Index
{
[Range(0, 1000, ErrorMessage = "hello")]
public int SelectedFavColor { get; set; }
public IEnumerable<SelectListItem> DropDownItems { get; set; }
}
public class Colors
{
public int ColorID { get; set; }
public string ColorName { get; set; }
}
}
I am passing some Dropdown values in the View.
Below is the Controller action:
public ActionResult Contact()
{
List<MvcApplication1.Models.Colors> l = new List<Models.Colors>();
l.Add(new Models.Colors { ColorName = "-1", ColorID = -1 });
l.Add(new Models.Colors { ColorName = "a", ColorID = 0 });
l.Add(new Models.Colors { ColorName = "b", ColorID = 2 });
l.Add(new Models.Colors { ColorName = "c", ColorID = 3 });
l.Add(new Models.Colors { ColorName = "d", ColorID = 4 });
l.Add(new Models.Colors { ColorName = "e", ColorID = 4 });
l.Add(new Models.Colors { ColorName = "f", ColorID = 4 });
var model = new MvcApplication1.Models.Index
{
DropDownItems = l.Select(i => new SelectListItem
{
Text = i.ColorName,
Value = i.ColorID.ToString()
})
};
ViewData["records"] = model.DropDownItems;
return View(model);
}
[HttpPost]
public ActionResult Contact(Index posted, FormCollection collection)
{
posted.SelectedFavColor = Convert.ToInt16(collection["SelectedFavColor"]);
return View(posted);
}
Why is the DropDown values Null in the POST action?

The DropDownItems is null because it is not being POSTed in your form. You are only creating a drop down menu with this: Html.DropDownListFor(x => x.SelectedFavColor, Model.DropDownItems), which is not enough to post the collection.
Posting a collection, however, is rather tricky; but you can look into this.
One last thing: you already have the collection (since you are passing it to the View), so technically, you don't even need to POST it back to the server.

The simple reason why the values of DropDown are null is because they are not sent (POSTed) with the form (You can easily check it with Firebug Firefox extension). You will need to collect (repopulate) the values of dropdown list again to see those values in view.
Suggestion:
Do not return the view immediately after the POST. Typical pattern in ASP-MVC apps is Post-Redirect-Get. It will help you to avoid unnecesary re-POSTs of the form (e.g. on browser refresh button) - Why should I use PRG.

Related

Passing Model data from View to Controller

I am trying to pass the Model data from a View (and PartialView within the View) back to the Controller upon HttpPost. (Adapted from Pass SelectedValue of DropDownList in Html.BeginForm() in ASP.NEt MVC 3)
Why? I want to show a list of assets each with a DropDownList and number of options. Upon submission of form to read the selected items from DropDownList.
My 2 (simplified) models:
public class Booking
{
public int BookingID { get; set; }
public int StoreID { get; set; }
...
public IEnumerable<AssetShort> Assets { get; set; }
}
and
public class AssetShort
{
public int AssetID { get; set; }
....
public int SelectedAction { get; set; }
public IEnumerable<SelectListItem> ActionList { get; set; }
}
In my Booking Controller > Create I build the List:
public ActionResult Booking(int id)
{
// get myBag which contains a List<Asset>
// booking corresponds to 'id'
var myAssets = new List<AssetShort>();
foreach (var a in myBag.Assets)
{
var b = new AssetShort();
b.AssetID = a.ID;
b.SelectedAction = 0;
b.ActionList = new[]
{
new SelectListItem { Selected = true, Value = "0", Text = "Select..."},
new SelectListItem { Selected = false, Value = "1", Text = "Add"},
new SelectListItem { Selected = false, Value = "2", Text = "Remove"},
new SelectListItem { Selected = false, Value = "3", Text = "Relocate"},
new SelectListItem { Selected = false, Value = "4", Text = "Upgrade"},
new SelectListItem { Selected = false, Value = "5", Text = "Downgrade"}
};
myAssets.Add(b);
};
var model = new BookingRequirementsViewModel
{
BookingID = booking.ID,
StoreID = booking.StoreID,
Assets = myAssets.ToList(),
};
return View(model);
My View:
#model uatlab.ViewModels.BookingRequirementsViewModel
#{
ViewBag.Title = "Booking step 2";
}
<h4>Your booking ref. #Model.BookingID</h4>
#using (Html.BeginForm("Booking2", "Booking", FormMethod.Post))
{
<fieldset>
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.StoreID)
#Html.Partial("_Assets", Model.StoreAssets)
<input type="submit" value="Cancel" class="btn btn-default" />
<input type="submit" value="Next" class="btn btn-default" />
</fieldset>
}
The Partial View includes
#foreach (var item in Model)
{
<tr>
<td>#item.Name</td>
<td>#item.Number</td>
<td>#Html.DropDownListFor(modelItem=>item.SelectedAction, item.ActionList)</td>
</tr>
}
So, all this works fine in the browser and I can select dropdowns for each asset listed but when I submit the only value posted back is the StoreID as it is in a "HiddenFor".
The booking2 controller has the model for a parameter:
public ActionResult Booking2(BookingRequirementsViewModel model)
{
//loop through model.Assets and display SelectedActions
}
Let me make it clear what the problems is - in Booking2 controller the Model is null when viewed in Debug mode and I get error "Object reference not set to an instance of an object."
Any ideas please how to pass back the Model to controller from view?
Regards
Craig
You need to create an EditorTemplate for AssetShort. I also suggest moving ActionList to the BookingRequirementsViewModel so your not regenerating a new SelectList for each AssetShort
The models you have posted aren't making sense. Your controller has var model = new BookingRequirementsViewModel { ..., Assets = myAssets.ToList() }; but in the view you refer to #Html.Partial("_Assets", Model.StoreAssets)? Are these 2 different properties. I will assume that StoreAssets is IEnumerable<AssetShort>
/Views/Shared/EditorTemplates/AssetShort.cshtml
#model AssetShort
<tr>
<td>#Html.DispayFor(m => m.Name)</td>
....
<td>
#Html.DropDownListFor(m => m.SelectedAction, (IEnumerable<SelectListItem>)ViewData["actionList"], "--Please select--")
#Html.ValidationMessageFor(m => m.SelectedAction)
</td>
</tr>
In the main view
#model uatlab.ViewModels.BookingRequirementsViewModel
....
#using (Html.BeginForm()) // Not sure why you post to a method with a different name
{
....
#Html.HiddenFor(m => m.StoreID)
#Html.EditorFor(m => m.StoreAssets, new { actionList = Model.ActionList })
....
}
In the controller
public ActionResult Booking(int id)
{
....
var model = new BookingRequirementsViewModel
{
BookingID = booking.ID,
StoreID = booking.StoreID,
Assets = myBag.Assets.Select(a => new AssetShort()
{
AssetID = a.ID,
SelectedAction = a.SelectedAction, // assign this if you want a selected option, otherwise the "--Please select--" option will be selected
....
})
};
ConfigureViewModel(model); // Assign select list
return View(model);
}
And a separate method to generate the SelectList because it needs to be called in the GET method and again in the POST method if you return the view. Note use the overload of DropDownListFor() to generate the option label (null value) as above, and there is no point setting the Selected property (the value of SelectedAction determines what is selected, not this)
private ConfigureViewModel(BookingRequirementsViewModel model)
{
model.ActionList = new[]
{
new SelectListItem { Value = "1", Text = "Add"},
....
new SelectListItem { Value = "5", Text = "Downgrade"}
};
}
and the POST
public ActionResult Booking(BookingRequirementsViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model); // Re-assign select list
return View(model);
}
// save and redirect
}
I recommend also making SelectedAction nullable with the [Required] attribute so you get client and server side validation
public class AssetShort
{
public int AssetID { get; set; }
....
[Required]
public int? SelectedAction { get; set; }
}

MVC2 html dropdownlist is invisible

I am just trying to populate a html.dropdown list using mvc2 in VS2008.
But the control is not displayed at all.
Here is my code
public ActionResult Index()
{
ViewData["Time"] = DateTime.Now.ToString();
var mdl = new List<SelectListItem>();
mdl.Add(new SelectListItem
{
Value = "1",
Text = "Module One"
});
mdl.Add(new SelectListItem
{
Value = "2",
Text = "Module Two"
});
ViewData["moduleList"] = new SelectList(mdl,"Value", "Text");
return View("MainMenu");
}
and here is the markup
<div>
<%Html.DropDownList("moduleList", (IEnumerable<SelectListItem>)ViewData["moduleList"]); %>
</div>
Where did i go wrong ?
You are best putting that stuff in your model so for example
in the controller
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Time"] = DateTime.Now.ToString(CultureInfo.InvariantCulture);
var mdl = new List<SelectListItem>
{
new SelectListItem
{
Value = "1",
Text = "Module One"
},
new SelectListItem
{
Value = "2",
Text = "Module Two"
}
};
ViewData["moduleList"] = new SelectList(mdl, "Value", "Text");
var model = new HomeModel
{
SelectedItem = 1,
items = mdl
};
return View(model);
}
}
Now create the model
namespace MvcApplication1.Models
{
public class HomeModel
{
public int SelectedItem { get; set; }
public IEnumerable<SelectListItem> items { get; set; }
}
}
your page will look like this on a test site
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.HomeModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2><%: ViewData["Message"] %></h2>
<p>
To learn more about ASP.NET MVC visit http://asp.net/mvc.
</p>
<div>
<%=Html.DropDownList("SelectedItem", Model.items)%>
</div>
</asp:Content>
Now an explanation, you have created a model for the view and this model is returned to the page by the controller the page is inheriting from a ViewPage which takes the generic argument of the model that was supplied to it by the controller
The markup is saying "give me a html drop down and mark the selected item as the first selected, the items come from the model (which is what your controller supplied it).
In the real world the data would come from your data layer and not directly in the controller (I like as little code in the controller as possible)
edit:
You have a typo for your example try this
<%= Html.DropDownList("moduleList") %>

List is not persisting through updatemodel

My generic list of products won't persist on the Post if I use UpdateModel but if I pass the ViewModel in as a parameter to my post action method it works? How can I get it to work through UpdateModel way?
I'm using asp.net fx3.5 mvc 1.0
Model
namespace PostingGenericListAndUpdateModel.ViewModels
{
public class Product
{
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class ProductViewModel
{
public int OrderId { get; set; }
public List<Product> Products { get; set; }
public ProductViewModel()
{
Products = new List<Product>();
Products.Add(new Product() { Name = "Widget 1", IsSelected = false });
Products.Add(new Product() { Name = "Widget 2", IsSelected = false });
}
}
}
View
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Index</h2>
<% using (Html.BeginForm())
{ %>
<% for (int i = 0; i < 2; i++)
{ %>
<%= Model.Products[i].Name %> <%= Html.CheckBox("Model.Products[" + i + "].IsSelected") %>
<% } %>
<input id="Submit1" type="submit" value="submit" />
<% } %>
</asp:Content>
Controller
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ProductViewModel model = new ProductViewModel();
return View(model, new string[] { "OrderId", "Products" });
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection form) //It will work if i accept the view model
{
ProductViewModel model = new ProductViewModel();
UpdateModel(model);
return View(model);
}
I see no reason for this parameter 'string sender'. Why do you need it?
The usual way to do this is indeed, by accepting the view model as a parameter to your Post method.
This is the way MVC passes the information from the view to your controller.
Your Post method line should be:
public ActionResult Index(ProductViewModel model)

How does Model binding with a selectlist work?

I'm having problems retrieving the values of a selectlist in my form collection. I've tried making a viewmodel with an attribute with the same name as the select list.
I'm honestly just realizing I REALLY don't understand how model binding works with selectlists. I've just been assuming that the following conventions apply:
Name the select list the same thing as the attribute on the model you want it to bind to.
Apart from that, I really don't get it. I've looked at several books on it and they're useless frankly.
How does a select list work with a) form collection and b) a particular model?
Here's an example:
Model:
public class MyViewModel
{
public string SelectedItemValue { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: Fetch those from a repository
Items = new SelectList(
new[]
{
new SelectListItem { Value = "1", Text = "Item 1" },
new SelectListItem { Value = "2", Text = "Item 2" },
new SelectListItem { Value = "3", Text = "Item 3" },
},
"Value",
"Text"
)
};
}
[HttpPost]
public ActionResult Index(string selectedItemValue)
{
// Here you get the selected value from the dropdown
return ...
}
}
View:
<% using (Html.BeginForm()) { %>
<%= Html.DropDownListFor(x => x.SelectedItemValue, Model.Items)
<input type="submit" value="OK" />
<% } %>

MVC Problems setting the selected item of the dropdownlist

In my controller I set the SelectedYear field as follows;
[Authorize(Roles = "Administrator, AdminAccounts")]
public ActionResult BankHoliday(int? id)
{
BankHolidayViewModel BankHolidayViewModel = new BankHolidayViewModel();
int year = DateTime.Now.Year;
if (id.HasValue)
year = id.GetValueOrDefault();
BankHolidayViewModel.SelectedYear = year;
BankHolidayViewModel.GetYearData(year);
return View(BankHolidayViewModel);
}
Whenever I step through the code, the SelectedYear value is always correct on the return statement. In the Year DropDownList below, this is the Value field that sets the selected item in the DropDownList;
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/AdminAccounts.master" Inherits="System.Web.Mvc.ViewPage<SHP.WebUI.Models.BankHolidayViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
BankHoliday
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="AdminAccountsContent" runat="server">
<h3>Bank Holiday Administration</h3>
<p>Select the year: <%: Html.DropDownListFor(model => model.SelectedYear, Model.YearList)%></p>
<script language="javascript" type="text/javascript">
$(function () {
$("#SelectedYear").change(function () {
var year = $("#SelectedYear").val();
$("#wholepage").load("/AdminAccounts/BankHoliday/" + year);
});
});
* EDIT: ADDED INFO *
The YearList is defined as follows;
public class BankHolidayViewModel
{
public IList<BankHolidayExtended> BankHolidays { get; set; }
public IList<BankHolidayYear> Years { get; set; }
public int SelectedYear = DateTime.Now.Year;
public IEnumerable<SelectListItem> YearList
{
get
{
return this.Years.Select(item => new SelectListItem
{
Text = item.Year.ToString(),
Value = item.Year.ToString()
});
}
}
* END EDIT *
For some reason when the View is rendered, there is no selected item in the dropdownlist. How do I fix this?
Your "Inherits" needs to inherit your ViewModel.
Inherits="System.Web.Mvc.ViewPage<ViewModels.BankHolidayViewModel>"
When you create the YearList, you also need to set the selected value. Something like
YearList = new SelectList(GetYears(), "year", "year", SelectedYear);
EDIT
In your view you can set the selected value:
<%= Html.DropDownListFor(x => x.SelectedYear, new SelectList(Model.YearList, "Value", "Text", Model.SelectedYear))%>
Or you can do it when you originally set up the Year List as I put above. GetYears() would return the years for the list.
public SelectList YearList { get; private set; }
public class BankHolidayViewModel(int year)
{
YearList = new SelectList(GetYears(), "Value", "Text", year);
Then when you return the view
return View(new BankHolidayViewModel(year));
I didn't really want to change my object to a SelectList, although I am sure that would have worked.
Instead I got the answer by changing the getter;
public IEnumerable<SelectListItem> YearList
{
get
{
return this.Years.Select(item => new SelectListItem
{
Text = item.Year.ToString(),
Value = item.Year.ToString(),
Selected = item.Year == this.SelectedYear
});
}
}

Resources