8

This is my first ever C# / MVC project and I am having problems binding to a model. I have read an applied Phil Haack's Post and have used EditorFor for all my partial views. Do I need a custom Model Binder? Please help

In brief, I have a list of weeks which contain a list of entries. These entries contain a list of hours

Action:

[HttpPost]
public ActionResult SubmitRecords(List<WeekCollection> itemData)
{
     //do stuff
     return View();
}

Model:

public class WeekCollection
{
    public WeekCollection()
    {
        this.OneWeek = new List<Entry>();
    }

    public List<Entry> OneWeek { get; set; }
}

[Bind(Exclude = "Task, Project")]
public class Entry
{
    public int ProjectId { get; set; }
    public virtual Projects Project { get; set; }

    public int TaskId { get; set; }
    public virtual Tasks Task { get; set; }

    public bool Billable { get; set; }
    public List<Hours> Gethours { get; set; }
}

public class Hours
{
    public float NumberOfHours { get; set; }
}

Views (Index)

//within partial view iteration with incrementing u (u++)
@using(@Html.BeginForm())
{ 
    @Html.EditorFor(m => m[u].OneWeek, "TimesheetWeek")
    <input type="Submit">
}

Views (TimesheetWeek)

@foreach (var value in Model)
{
    v++;
    if (Model.All(x => x.projectId == 0))
    {
        @Html.DropDownListFor(p => p[v].projectId, (IEnumerable<SelectListItem>)projectList, "Select Project", new { @class = "notSelect" })
        @Html.DropDownListFor(t => t[v].taskId, (IEnumerable<SelectListItem>)taskList, "Select Task", new { @class = "notSelect" })
     }
     else
     {
        if (value.projectId != 0)
        {    
           @Html.DropDownListFor(p => p[v].projectId, (IEnumerable<SelectListItem>)projectList, new Dictionary<string, Object> { { "class", "SelectDrop" }, { "data-selectHead", value.projectId } })
           @Html.DropDownListFor(t => t[v].taskId, (IEnumerable<SelectListItem>)taskList, new Dictionary<string, Object> { { "class", "SelectDrop" }, { "data-selectHead", value.taskId } })
        }
     }

     @Html.CheckBoxFor(b => b[v].billable)
     @Html.EditorFor(h => h[v].gethours, "HoursDisplay")

     @value.gethours.Sum(a => a.numberOfHours)
     }

View (HoursDisplay)

@for (var i = 0; i < Model.Count(); i++)
{
    @Html.TextBoxFor(m => m[i].numberOfHours)
}

Model displays all the data correctly and the form data output posted is as follows:

[0].OneWeek.[0].projectId:1
[0].OneWeek.[0].taskId:1
[0].OneWeek.[0].billable:true
[0].OneWeek.[0].billable:false
[0].OneWeek.[0].gethours.[0].numberOfHours:0
[0].OneWeek.[0].gethours.[1].numberOfHours:5
[0].OneWeek.[0].gethours.[2].numberOfHours:7
[0].OneWeek.[0].gethours.[3].numberOfHours:6
[0].OneWeek.[0].gethours.[4].numberOfHours:4
[0].OneWeek.[0].gethours.[5].numberOfHours:8
[0].OneWeek.[0].gethours.[6].numberOfHours:0

I thought I got the indexing right but currently get an empty Oneweek in the action. What am I doing wrong? Any assistance is appreciated. (Some repetition and HTML has been removed)

1 Answer 1

7

Your prefixes are wrong. For example:

[0].OneWeek.[0].projectId:1

should be:

[0].OneWeek[0].projectId:1

You have an extra dot (.). Read Phil Haack's article once again for the correct syntax when binding with lists.

You have the same problem with the gethours.[0] part.

I would recommend you using the standard editor templates conventions and avoid writing any foreach loops and dealing with indexes:

~/Views/Home/Index.cshtml:

@model List<WeekCollection>
@using(Html.BeginForm())
{ 
    @Html.EditorForModel()
    <input type="Submit" />
}

~/Views/Home/EditorTemplates/WeekCollection.cshtml:

@model WeekCollection
@Html.EditorFor(x => x.OneWeek)

~/Views/Home/EditorTemplates/Entry.cshtml:

@model Entry
...
Sign up to request clarification or add additional context in comments.

1 Comment

That worked perfectly thank you. I didn't realise just how useful the EditorFor actually is and as a result the view models are far simpler. Very useful knowledge. Thanks + Merry Christmas

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.