views:

166

answers:

3

Hello guys/girls,

Here is my situation -

I have two nested view models:

  1. <%=Html.EditorFor(x => x.DisplayEntitiesWithRadioboxesViewModel)%><br />

Which sit within their parent (StructureViewModel), I can populate the nested ViewModels easily and pass it through to the main View:

Within the Controller - Example

var moveDepartment = new StructureViewModel();
moveDepartment.DisplayEntitiesWithRadioboxesViewModel = fullDepartmentList.Select(x => new DisplayEntityViewModel
            {
                Id = x.Id,
                Path = x.Path,
                PathLevel = x.PathLevel,
                Description = x.Description,
            });

return View(moveDepartment);

EditorTemplete - Example

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Site.Areas.Administration.ViewModel.DisplayEntityViewModel>>" %>
<table class="aligncenter"><%
  if (Model != null)
  {
    foreach (var entity in Model)
    {%>
      <tr class="tRow">
        <td style="text-align:left; text-indent:<%=Html.Encode(entity.PathLevel)%>em">
          <%=Html.Encode(entity.Description)%>
          <%=Html.RadioButton("radiobutton",entity.Id)%>
        </td>              
      </tr><%    
    }
  }%>
</table>


namespace Site.Areas.Administration.ViewModel
{
    public class DisplayEntityViewModel
    {
        public int Id { get; set; }
        public string Path { get; set; }
        public string PathLevel { get; set; }
        public string Description { get; set; }   
    }
}

However when I try to pull back this information the nested ViewModels are null:

[HttpPost]
public ActionResult Move(StructureViewModel StructureViewModel)

When I hover over StructureViewModel it only contains data set at the parent ViewModel. For example: a hidden value can been seen but DisplayEntitiesWithRadioboxesViewModel = null.

The only way I know how to access the DisplayEntitiesWithRadioboxesViewModel is to use FormCollection and iterate throught the FormCollection and pull out the information I need from the nested ViewModels.

This however just doesn't seem right, as I have found at I then have to re-populate the DisplayEntitiesWithRadioboxesViewModel with the values from the FormCollection, if for example an error has occured and the user needs to be sent back to the same View.

I have tried searching the web/books but cannot find a solution.

Is there a better way?

Thanks in advance for any help.

And why did you use an EditorFor for a simple dropdown, which is easily to use with DropDownFor

This has now been altered to use the DropDownFor.

what is the Key of the DisplayEntitiesWithRadioboxesViewModel value in FormCollection

{string[3]}
[0] = "DisplayEntitiesWithRadioboxesViewModel.radiobutton"
[1] = "Action"
[2] = "OldParentId"

Clare :-)

A: 

Can you tell me how the EditorFor looks exactly? And why did you use an EditorFor for a simple dropdown, which is easily to use with DropDownFor.

what is the Key of the DisplayEntitiesWithRadioboxesViewModel value in FormCollection

If I understand it correctly, you have a View, with some parent-info, and at the same time multiple iterations of these 2 fields in the same view. Is that right?

Then I know how to fix this.

Stefanvds
Hello Stefanvds, I have altered my question above to answer your questions. I have a Parent View which holds one instance of the DisplayEntitiesWithRadioboxesViewModel. The DisplayEntitiesWithRadioboxesViewModel is IEnumerable. Thanks ClareDetails however I only use to nested Views once within this
ClareBear
it is logic that the key for your radiobutton is 'DisplayEntitiesWithRadioboxesViewModel.radiobutton' because in your editorFor you have a radiobutton with its Name 'radiobutton'. If I get it right, everything works, only the radiobuttons value does not get parsed in your viewmodel. I think the best way is to create a RadioButtonList helper as described here: http://geekswithblogs.net/michelotti/archive/2009/08/05/mvc-radiobuttonlist-html-helper.aspx . I did the same before with a CheckBoxList and that worked out well. also, maybe replace radiobuttonlist with dropdown?
Stefanvds
did you manage to fix it? :)
Stefanvds
+2  A: 

Your problem is pretty common and somewhat easy to fix once you understand how it works.

Right now you have a view model that has a property which is an IEnumerable<T> (doesn't matter what the generic parameter is).  You are trying to pass the items to the view and populate the IEnumerable<T> with the same values when the response comes back, using the values originally written to the page, and augmented with the selected item (at least from the code you have posted anyway, it would help for you to state your exact intention in the question).  The problem you have here is that you must send those values to the page in a way in which they can be returned.

Let me just say now that you probably should NOT be using this technique.  It is typically a much better idea to return the selection only and generate the list again if you need to server side.

From the looks of things, you want to return the whole list and then look for the item that is selected, which is after all the point of a drop down or radio button group.  In order to get the selection back, the parameter to your controller action must have properties which match the variables passed back in.  In this case, it looks like you are using the parameter name radiobutton for all of your radio buttons (the same hold true for drop down list, only it uses the name of the list).  Which ever one is selected, the value associated with it is returned with that name.  The MVC framework takes care of trying to find the appropriate action which has as many names specified as possible.  

What you need to use for your action parameter is a new class that contains a property for all of the field names being submitted back to the server!  Or of course you could simply add the radiobutton property to your StructureViewModel too.  In fact, you'll notice that it is trying to set that value already, only it doesn't currently exist on your view model.  You still will not receive the original list back however, but thats okay, because even if you did receive the original list back, you have no identifier on it to let you know which item was selected!

Hopefully this helps you understand what is going on, if you have more questions, please ask.

NickLarsen
+1  A: 

I would recommend you using strongly typed helpers everywhere so that you don't have to worry about naming your controls. Here's how to proceed:

Models:

public class DisplayEntityViewModel
{
    public int Id { get; set; }
    public string Path { get; set; }
    public string PathLevel { get; set; }
    public string Description { get; set; }
}

public class StructureViewModel
{
    public IEnumerable<DisplayEntityViewModel> DisplayEntitiesWithRadioboxesViewModel  { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var moveDepartment = new StructureViewModel();
        moveDepartment.DisplayEntitiesWithRadioboxesViewModel = new[] 
        {
            new DisplayEntityViewModel
            {
                Id = 1,
                Path = "some path 1",
                PathLevel = "some path level 1",
                Description = "some description 1"
            },
            new DisplayEntityViewModel
            {
                Id = 2,
                Path = "some path 2",
                PathLevel = "some path level 2",
                Description = "some description 2"
            },
        };
        return View(moveDepartment);
    }

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

Main View (~/Views/Home/Index.aspx):

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SomeNs.Models.StructureViewModel>" %>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<% using (Html.BeginForm()) { %>
    <table class="aligncenter">
        <%= Html.EditorFor(x => x.DisplayEntitiesWithRadioboxesViewModel) %>
    </table>
    <input type="submit" value="Go" />
<% } %>

</asp:Content>

Editor Template (~/Views/Home/EditorTemplates/DisplayEntityViewModel.ascx)

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ToDD.Models.DisplayEntityViewModel>" %>

<tr class="tRow">
    <td style="text-align:left; text-indent:<%=Html.Encode(Model.PathLevel)%>em">
        <%= Html.Encode(Model.Description) %>

        <!-- Remember that you need to place input fields for each property
             that you expect to get back in the submit action
        -->
        <%= Html.HiddenFor(x => x.Description) %>
        <%= Html.TextBoxFor(x => x.Path) %>
    </td>              
</tr>

Now submit the form and everything should be bound correctly. An important thing to note is that the editor template is strongly typed to DisplayEntityViewModel and not IEnumerable<DisplayEntityViewModel> as in your case. When in your main view you write:

<%= Html.EditorFor(x => x.DisplayEntitiesWithRadioboxesViewModel) %>

the framework automatically detects that the property is a collection and will call the editor template for each item of this collection so you no longer need to loop through the elements which makes your code more elegant.


UPDATE:

Using dropdown lists is also very easy: checkout this answer.

Darin Dimitrov
Although this does return all values submitted to the browser back to the server, it still fails to grab the selected value back, which I believe is his misunderstanding.
NickLarsen
@NickLarsen, it is not clear to me about the dropdown. After her edit the OP states that she used a dropdown but didn't show her code and still talks about `radiobutton`. Using the `DropDownListFor` helper method works perfectly fine with editor templates and generates correct names so I don't understand what do you mean when you say that this *still fails to grab the selected value back*.
Darin Dimitrov
@Darin: If she is creating a drop down list or radio button list, she is trying to select a single item from a list.  From my understanding, her problem is retrieving the selected value from using either method and has to parse the form data to retrieve it, but would like to retrieve it as a property of her view model.  The simple fix is to add the name of the list used, which in this case is `radiobutton` as a property to her view model and she will no longer have to dig in the form data for the value she wants.
NickLarsen