views:

49

answers:

2

Hey everyone,

I have a strongly typed view of type ProductListingViewModel which in turn contains a ProductViewModel. (both custom view models).

I have some form elements on my page and these are created like so:

<%: Html.DropDownListFor(m => m.ProductViewModel.CategoryId, Model.Categories)%>

which generates the HTML:

<select name="ProductViewModel.CategoryId" id="CategoryId">

With the default model binding I expected that when I post to my controller action which accepts a parameter of type ProductListingViewModel, that it'd know to populate the ProductViewModel.CategoryId with the relevant data.

The name of the select list seems to indicate that it knows there's a ProductViewModel with a CategoryId property however when I post to my controller method, the ProductViewModel is null. If I create this during construction of the ProductListingViewModel then it's no longer null but the default binder doesn't seem to be populating the properties as I expected.

Is this a case for a custom model binder or am I just missing something fundamental?

Cheers.

A: 

Let me try to summarize (correct me if I am wrong).

Model:

public class ProductListingViewModel
{
    public ProductViewModel ProductViewModel { get; set; }
    public IEnumerable<SelectListItem> Categories { get; set; }
}

public class ProductViewModel
{
    public string CategoryId { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new ProductListingViewModel
        {
            Categories = new SelectList(new[]
            {
                new { Value = "1", Text = "category 1" },
                new { Value = "2", Text = "category 2" }
            }, "Value", "Text")
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(ProductListingViewModel model)
    {
        return View(model);
    }
}

View:

<% using (Html.BeginForm()) { %>
    <%: Html.DropDownListFor(m => m.ProductViewModel.CategoryId, Model.Categories)%>
    <input type="submit" value="OK" />
<% } %>

Now when you submit the form you will get:

model.ProductViewModel.CategoryId = the id that was selected in the drop down list

Isn't what you are after?

Darin Dimitrov
Hey Darin. That looks almost identical to what I'm doing. The problem is, when I submit the form, model.ProductViewModel is null. If I create ProductViewModel in the constructor of ProductListingViewModel then it isn't null anymore but it does not contain any of the values I posted.
Jamie Dixon
I guess that it's this *almost identical* that poses a problem because what I've posted works.
Darin Dimitrov
I've made it exactly the same as your example but I still get a null value back. Odd. I must be doing something wrong but I'm unsure what.
Jamie Dixon
Also Darin, here's a bit a don't really get. In your example you don't create a ProductViewModel before passing the ProductListingViewModel to the View. When I try this i get an exception because m.ProductViewModel is null.
Jamie Dixon
@Jamie, you could try my sample: http://drop.io/0lrzd3x .You are correct about me not initializing the ProductViewModel property in the POST action. That's definitely a mistake but I thought that your problem was getting the selected category ID from the model binder inside this action. What you do after was off-topic. If you intend to show the same view than you need to repopulate the property the same way you are doing it in the GET action. Of course you should be using a repository to avoid repetition :-)
Darin Dimitrov
Hey Darin. Your example works perfectly and I'm replicating the process that you're doing but my post action method still contains null for the ProductViewModel. Really confused but I'll keep at it. Thanks for your help mate. Much appreciated.
Jamie Dixon
Darin, how's this for a bit odd. The method that receives my post looks as follows:public ActionResult DoAdd(ProductListingViewModel productViewModel) {If i rename the parameter 'productViewModel' to 'x', suddenly it all works. Any idea why this might be?
Jamie Dixon
Yes, that's because your parameter cannot be named the same as a property name in the model as the model binder cannot distinguish between them.
Darin Dimitrov
That'll be it then. Thanks mate. All problems resolved by a simple rename of a parameter. I didn't know that params couldn't be named that way. I thought at least the casing would count for something but it's good to know :-) Cheers for all ur help mate. If you wanna add that pieces of info to ur answer i'll accept it.
Jamie Dixon
A: 

It seems to me that the default binder should work in this case.

Did you try using Fiddler for checking the data sent from the client?

What exactly is the signature of the controller action?

Corina