Andrew Simpson
Andrew Simpson

Reputation: 7324

Return nested class objects within my Model back to my controller

I have a MVC c# web app.

I have a Model which is populated in my controller and is rendered to my View.

This Model has nest class List object to the 2nd level.

This Model allows for checkboxes etc to be rendered in my View which i use Razor to do.

Ultimatley, I wish the User to be able to check/uncheck etc the checkbox values.

But for now, proof of concpet I just want to call a method on my controller to return the default values.

One of the ways i saw to persist the Model data was to use Hidden Fields.

This has worked to the 1st level of nested class objects but not the next layer.

To explain better these are my Models I am using in this View:

[Serializable]
[XmlRoot("preferences")]
public class PreferencesModel
{
    /// <summary>
    /// Message to display to user on UI
    /// </summary>
    [XmlIgnore]
    public string MessageToUser { get; set; }
    /// <summary>
    /// Stores Preferences
    /// </summary>
    [XmlElement("section")]
    public IList<SectionModel> SectionModel { get; set; }
}

[Serializable]
[XmlRoot("section")]
public class SectionModel
{
    /// <summary>
    /// Name of Section (for Grouping Purposes)
    /// </summary>
    [XmlAttribute("name")]
    public string Name { get; set; }
    /// <summary>
    /// List of Preferences for this section
    /// </summary>
      [XmlElement("preference")]
    public List<PreferenceModel> PreferenceModel { get; set; }
}

[Serializable]
[XmlRoot("preference")]
public class PreferenceModel
{
    /// <summary>
    /// Type of HTML Control ie radio button, textbox
    /// </summary>
    [XmlAttribute("type")]
    public string Type { get; set; }
    /// <summary>
    /// Name of Preference
    /// </summary>
    [XmlAttribute("name")]
    public string Name { get; set; }
    /// <summary>
    /// 
    /// </summary>
    [XmlAttribute("value")]
    public string Value { get; set; }
     /// <summary>
     /// 
     /// </summary>
    [XmlElement("options")]
    public List<Option> Options { get; set; }
}

[Serializable]
[XmlRoot("options")]
public class Option
{
    /// <summary>
    /// 
    /// </summary>
    [XmlAttribute("OptionName")]
    public string OptionName { get; set; }
    /// <summary>
    /// 
    /// </summary>
    [XmlAttribute("OptionValue")]
    public string OptionValue { get; set; }
}

and this is my View relating to the Model bit:

@for (int i = 0; i != Model.SectionModel.Count; i++)
{
    @Html.HiddenFor(m => m.SectionModel[i].Name, "Name")
    <div style="clear: left; padding-left: 20px;">
        <h1>@Html.Label(Model.SectionModel[i].Name)</h1>
        <div class="clear"></div>
        @for (int j = 0; j != Model.SectionModel[i].PreferenceModel.Count; j++)
        {
            @Html.HiddenFor(m => m.SectionModel[i].PreferenceModel[j].Type, "type")
            @Html.HiddenFor(m => m.SectionModel[i].PreferenceModel[j].Name, "Name")
    

            switch (Model.SectionModel[i].PreferenceModel[j].Type)
            {
                case "check":
                    <p>@Html.Label(Model.SectionModel[i].PreferenceModel[j].Name)</p>
                    for (var r = 0; r != Model.SectionModel[i].PreferenceModel[j].Options.Count(); r++)
                    {
                        if (Model.SectionModel[i].PreferenceModel[j].Value == Model.SectionModel[i].PreferenceModel[j].Options[r].OptionValue)
                        {
                            @Html.CheckBox(Model.SectionModel[i].PreferenceModel[j].Options[r].OptionName,true)
                        }
                        else
                        {
                             @Html.CheckBox(Model.SectionModel[i].PreferenceModel[j].Options[r].OptionName, false)
                        }
                        @Html.LabelFor(x => x.SectionModel[i].PreferenceModel[j].Value, Model.SectionModel[i].PreferenceModel[j].Options[r].OptionName)
                    }
                    break;
                case "radio":
                    <b>@Html.Label(Model.SectionModel[i].PreferenceModel[j].Name)</b>
                        for (var r = 0; r != Model.SectionModel[i].PreferenceModel[j].Options.Count(); r++)
                        {
                            @Html.HiddenFor(m => m.SectionModel[i].PreferenceModel[j].Options, "option")
                            @Html.HiddenFor(m => m.SectionModel[i].PreferenceModel[j].Options[r].OptionValue,"OptionValue")
                            @Html.HiddenFor(m => m.SectionModel[i].PreferenceModel[j].Options[r].OptionName, "OptionName")

                            if (Model.SectionModel[i].PreferenceModel[j].Value == Model.SectionModel[i].PreferenceModel[j].Options[r].OptionValue)
                            {
                                @Html.RadioButtonFor(m => m.SectionModel[i].PreferenceModel[j].Value, Model.SectionModel[i].PreferenceModel[j].Options[r].OptionValue, new
                                {
                                    @checked = true
                                })
                            }
                            else
                            {
                                @Html.RadioButtonFor(m => m.SectionModel[i].PreferenceModel[j].Value, Model.SectionModel[i].PreferenceModel[j].Options[r].OptionValue)
                            }
                            @Html.LabelFor(x => x.SectionModel[i].PreferenceModel[j].Value, Model.SectionModel[i].PreferenceModel[j].Options[r].OptionName)
                        }
                    break;
                case "text":
                    <b>@Html.Label(Model.SectionModel[i].PreferenceModel[j].Name)</b>
                    @Html.TextBoxFor(m => m.SectionModel[i].PreferenceModel[j].Value)
                    break;
                case "textarea":
                   <b> @Html.Label(Model.SectionModel[i].PreferenceModel[j].Name)</b>
                    @Html.TextAreaFor(m => m.SectionModel[i].PreferenceModel[j].Value)
                    break;
                case "combo":
                   <b>@Html.Label(Model.SectionModel[i].PreferenceModel[j].Name)</b>
                    var options = Model.SectionModel[i].PreferenceModel[j].Options;
                    var items = new SelectList(options, "OptionValue", "OptionName");
                    @Html.DropDownList("Options",items,Model.SectionModel[i].PreferenceModel[j].Value)
                    @Html.Hidden("Options",Model.SectionModel[i].PreferenceModel[j].Options)
                   break;
                default:
                    <div>test dud</div>        
                    break;
            }
             <div class="clear"></div>
        }
    </div>
}

setting a break point then the user submits the View/Model back I get this:

enter image description here

As you can see the Option values (just test for the Radio buttons at this stage) are empty.

what should I do or is there a 'cleaner' way to do this?

Thanks

Upvotes: 0

Views: 321

Answers (1)

user3559349
user3559349

Reputation:

You need to remove the following from the view

@Html.HiddenFor(m => m.SectionModel[i].PreferenceModel[j].Options, "option")

Options is a complex property (typeof List<Option>) and the html your generating is

<input type="hidden" value="System.Collections.Generic.List[yourAssembly.Option]" length="6" ... />

The value attribute is because its the .ToString() value of the property, and it cannot be bound to your collection so binding fails. Note the length="6" is because the 2nd parameter of HiddenFor() adds html attributes and there are 6 characters in "options"

Upvotes: 2

Related Questions