3

I've got troubles with MVC4 and Editor Templates when I get the post back after press the submit button. This is my model :

public class Form
{
    public Form()
    {
        this.Rows = new List<Row>();
    }

    public List<Row> Rows { get; set; }

    public int Id { get; set; }
}

public class Row
{
    public Row()
    {
        this.Label = string.Empty;
        this.Type = string.Empty;
    }

    public string Label { get; set; }

    public string Type { get; set; }

    public int Id { get; set; }
}

public class SimpleRow : Row
{
    public SimpleRow()
    {
        this.Value = string.Empty;
    }

    public string Value { get; set; }
}

public class DropDownRow : Row
{
    public DropDownRow()
    {
        this.Content = new List<ContentDropDown>();
    }

    public List<ContentDropDown> Content { get; set; }
}

public class ContentDropDown
{
    public ContentDropDown()
    {
        this.Title = string.Empty;
        this.Selected = false;
    }

    public string Title { get; set; }

    public bool Selected { get; set; }

    public int Id { get; set; }
}

This is my controller :

public class FormController : Controller
{
    [Authorize]
    public ActionResult Index()
    {
        return this.View();
    }

    [HttpPost]
    [Authorize]
    public ActionResult Index(HttpPostedFileBase file)
    {
        this.ViewBag.Title = "Formulaire Collaborateur";

        if (file != null && file.ContentLength > 0)
        {
            return this.View(SerialisationHelper.DeserializeFromStream<Form>(file.InputStream));
        }

        return this.View();
    }

    [HttpPost]
    [Authorize]
    public ActionResult EmailForm(Form updatedForm)
    {
        Form f = updatedForm; // Here, The parameter updatedForm have just is first row filled with empty strings and the other rows null

        return this.View("Index");
    }
}

My Index.cshtml :

@model Utils.FormObject.Form
@{
    ViewBag.Title = "Index";

}
@using (Html.BeginForm("EmailForm", "Form", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <fieldset>
        <p>
            @for (int i = 0; i < Model.Rows.Count; i++)
            {
                @Html.HiddenFor(x => x.Id)
                @Html.EditorFor(x => x.Rows[i])    
            }
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
    <br />
}

my others views in a sub-folder called EditorTemplates : Row :

@model Utils.FormObject.Row
@{
    ViewBag.Title = "Row";
}
@Html.HiddenFor(x => x.Id)
@Html.EditorFor(x => x)

SimpleRow :

@model Utils.FormObject.SimpleRow

@{
    if (Model.Type.Equals("String"))
    {
        <br />
        @Html.HiddenFor(x => x.Id)
        @Html.DisplayFor(modelItem => modelItem.Label)
        @Html.EditorFor(modelItem => modelItem.Value)
    }
    <br/>
}

DropDownRow :

@model Utils.FormObject.DropDownRow
@{
    ViewBag.Title = "DropDownRow";
}
@* @Html.DropDownListFor(x => x, new SelectList(Model.Content, "Title", "Title"))*@
@{
    @Html.HiddenFor(x => x.Id)
    @Html.LabelFor(item => item.Label)
    var list = new List<SelectListItem> { new SelectListItem { Value = string.Empty, Text = "--please select--", Selected = false } };
    foreach (var row in Model.Content)
    {
        list.Add(new SelectListItem { Text = row.Title, Selected = row.Selected });
    }
    @Html.DropDownListFor(x => x, list)
}

and ContentDropDownRow :

@model Utils.FormObject.ContentDropDown
@{
    ViewBag.Title = "ContentDropDown";
}
@Html.HiddenFor(x => x.Id)
@Html.LabelFor(x => x.Title)

My Form is approximately well displayed (in fact the label of the dropdown lists are displaying 'Label' instead of the Label of the property ...). The problem is when I press the submit button the post back value 'updatedForm' is not filled. The first row Row[0] have its labels filled by empty strings and the others rows are null. I tried this but I didn't managed to make it works.

I'm a little lost here, I don't understand why it refuse to work.

PuK

PuK
  • 305
  • 1
  • 5
  • 15

2 Answers2

3

You seem to be using an Editor Template to render your Row objects but I don't think you're using it correctly. If you have an Editor Template for a single Row object you just need to render the Row collection from your Index.cshtml by passing the collection to the template. Here's an Editor Template for your Row:

@model Utils.FormObject.Row
@{
    ViewBag.Title = "Row";
}
@Html.HiddenFor(x => x.Id)
@Html.EditorFor(x => x.Label)
@Html.EditorFor(x => x.Type)

Render the Row collection from the Index.cshtml view - no need to loop as the Editor Template will automagically render each Row in the collection and name them correctly:

@model Utils.FormObject.Form
@{
    ViewBag.Title = "Index";
}
<fieldset>
    <p>
        @Html.HiddenFor(x => x.Id)
        @Html.EditorFor(x => x.Rows)    
    </p>
    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>

This should create fields like below which will be correctly named and bound as part of your Form object when posted back to your Controller:

Rows[0].Id
Rows[0].Label
Rows[0].Type
Rows[1].Id
Rows[1].Label
Rows[1].Type

See my answer to this question:

Pass values from multiple partial views

Small write up on Editor Templates here:

codenodes.wordpress.com - MVC3 Editor Templates

Community
  • 1
  • 1
Ciarán Bruen
  • 5,221
  • 13
  • 59
  • 69
1

Thank you, sorry for answering this question two months after but better late than never !

I tried this, but it don't rendered as I wanted it to be rendered. This way make the property name as label and the property value as editable field. But I wanted several treatments before. I used the HiddenFor command to bind user inputs to my object like that :

<p>
    @Html.HiddenFor(x => x.ID)
    <h3> @Html.DisplayFor(x => x.Title)</h3>
    @Html.HiddenFor(x => x.Title)
</p>

Because, in this example, I didn't want the "Title" to be editable. I'm not sure this is the way it should be use but it works like that.

PuK
  • 305
  • 1
  • 5
  • 15
  • I'm not exactly sure what you mean but if you don't want a field to be editable then it's fine to use DisplayFor instead of EditorFor. You shouldn't need a HiddenFor for x.Title – Ciarán Bruen Sep 18 '12 at 20:57