问题
This is my model:
public class IndexViewModel
{
public FilterConditions conditions { get; set }
public IEnumerable<SelectListItem> Countries { get; set }
}
public class FilterConditions
{
public string condition11 { get; set }
// ...
}
And I have an Index action method like so:
public ActionResult Index()
{
var model = new IndexViewModel();
// fill the model here with default values
return View(model);
}
The view renders a form with the filterconditions as input types.
Now I want the post back from that form be handled by this action method:
[HttpPost]
public ActionResult Index(FilterConditions model)
{
// do some magic with model and return another view here
}
and this actually works (I put a breakpoint in the method, and it gets called), but the properties of my model are always empty (default values), while they should contain the values which were posted by the form.
When I modify the action method like this:
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
// do some magic with model.conditions and return another view here
}
It all works like it should, but this is not "right" (IMHO), as I don't need the ´Countries´ list on return, I only need the selected country (which is one of the conditions).
What is a nice (best practice) way to make this work without having to take the whole original viewmodel as an input parameter?
Btw, I'm using ASP.NET MVC 2 (I don't think it really matters, as I think it's the same problem in MVC 3, but I'm not entirely sure of that).
(I have been looking around the internet for "best practices" regarding dropdownlists and viewmodels within asp.net mvc, but the different recommendations I found didn't really line up with each other, and a lot is already outdated as well. I didn't find an "official" best practice around this. I hope I'm going in the right direction (having the list as part of my viewmodel), feel free to correct me on this matter if I'm not. Also feel free to point me to "endorsed best practices" about this if you know of any.)
Update:
I found out that I can use the [Bind] attribute with a Prefix of "filterconditions". And this indeed works for this view. But my original problem (I admit, it was not included in my question) is not solved.
It happens that this particular action method is also called from another view (it is an ajax call) where it doesn't have that prefix, in that case it doesn't work any more now. Any suggestions?
回答1:
I've found the solution.
Apparently, when I use the same name for the parameter variable as the name of the type (the case doesn't have to match), like this:
[HttpPost]
public ActionResult Index(FilterConditions filterConditions)
{
// do some magic with model and return another view here
// now the filterConditions variable actually contains values!
}
Everything works like it should (the values of my filterConditions are not empty/null anymore). Apparently, the default modelbinder uses the name of the parameter as the potential prefix for the binding.
I'm glad I found out, but it would be nice if this is more clearly documented somewhere. It's not obvious at all.
Edit: On request: this is the code in my view (aspx):
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyProject.Models.IndexViewModel>" %>
<%-- ... more stuff here ... --%>
<% using (Html.BeginForm())
{%>
<%= Html.ValidationSummary(true)%>
<fieldset>
<div class="editor-label">
<%= Html.LabelFor(model => model.FilterConditions.Country)%>
</div>
<div class="editor-field">
<%= Html.DropDownListFor(model => model.FilterConditions.Country, Model.Countries)%>
<%= Html.ValidationMessageFor(model => model.FilterConditions.Country)%>
</div>
<div class="editor-label">
<%= Html.LabelFor(model => model.FilterConditions.Make)%>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.FilterConditions.Make)%>
<%= Html.ValidationMessageFor(model => model.FilterConditions.Make)%>
</div>
<%-- ... more fields inserted here ... --%>
<p>
<input type="submit" value=" Search... " />
</p>
</fieldset>
<% } %>
回答2:
Hi fretje: Now I can use your way to solve your problem, First I have two Models "IndexViewModel" & "Index", and the DropDownList(it's doesn't matter, just offer the DropDown items):
public class IndexViewModel : Index
{
//public int value { get; set; }
public List<System.Web.Mvc.SelectListItem> items { get; set; }
}
public class Index
{
public int value { get; set; }
}
class DropDownList
{
public List<System.Web.Mvc.SelectListItem> GetDropDownList()
{
List<System.Web.Mvc.SelectListItem> result = new List<System.Web.Mvc.SelectListItem>();
result.Add(new System.Web.Mvc.SelectListItem
{
Value = "1",
Text = "Apple"
});
result.Add(new System.Web.Mvc.SelectListItem
{
Value = "2",
Text = "Milk"
});
return result;
}
}
And two Controllers is Test() and Test(Models.Index), I pass the IndexViewModel and postback IndexModel:
public ActionResult Test()
{
var result =
new Models.IndexViewModel
{
value = 1,
items = new Models.DropDownList().GetDropDownList()
};
return View(result);
}
[HttpPost]
public ActionResult Test(Models.Index posatback)
{
return View();
}
The View of Test() is:
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-field">
<%: Html.DropDownListFor(m=>m.value, Model.items )%>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
that's work! thank you fretje, I learned one more tech. : )
Maybe you can try
[HttpPost]
public ActionResult Index([Bind(Exclude="Countries")]IndexViewModel model)
{
// do some magic with model.conditions and return another view here
}
Hi~ You don't need to combine the whole SelectListItem to ViewModel, actually your ViewModel just only have a field to store user's choise, integer or string, then use DropDownListFor like:
<%: Html.DropDownListFor(item.WeaponID, MyApplication.Models.DropDownList.GetDropDownList() )%>
please see my post in my blogspot, I use a very simple example to explain: http://maidot.blogspot.com/2011/04/aspnet-mvc-viewdropdownlistfor.html
let me know if you have any problems : )
来源:https://stackoverflow.com/questions/5755928/how-to-let-the-post-action-method-take-a-subobject-of-the-original-viewmodel-as