3

I have the following model classes (classes simplifies for the purpose of this question):

public class Lesson
{
    public Guid Id {get;set;}
    public string Name {get;set;}
    public List<ExerciseForPupil> Exercises {get;set;}

}
public class ExerciseForPupil
{
    public Guid Id {get;set;}
    public string Name {get;set;}
    public List<ExerciseItemForPupil> ExerciseItems {get;set;}
}
public class ExerciseItemForPupil
{
    public Guid Id {get;set;}
    public string Content {get;set;}
    public string UserValue {get;set;}
}

Now, I want users to be able to fille "UserValue" value for each exercise in the lesson. Let's say there are 5 exercises for the lesson. I render these exercises as follows

@Html.EditorFor(x=>x.Lesson.Exercises)

Which renders an EditorTemplate which looks as follows: @model MyNamespace.ExerciseForPupil

@using (Html.BeginForm("ScoreExercise", "SharedLesson", FormMethod.Post))
{
    @Html.Hidden("Id", Model.Id)
    @if (Model.ExerciseItems != null)
        {
            foreach (var exerciseItem in Model.ExerciseItems)
                {
                        @Html.EditorFor(x => exerciseItem, "ExerciseItemForPupil")
                }
        }
    <input type="submit" value="Submit"/>
}

I also have EditorTemplate for "ExerciseItemForPupil":

@model MyNamespace.ExerciseItemForPupil
@Html.HiddenFor(model => model.Id)
@Html.TextBoxFor(model => model.UserValue)

Problem: As can be seen there will be multiple forms on the page. My "ScoreExercise" action is as follows:

public ActionResult ScoreExercise(ExerciseForPupil exercise)
{
    //exercise.ExerciseItems is NULL
}

But my nested collection on the second level (ExerciseItems) is null. What am I doing wrong?

UPDATE I've changed the code according to @MysterMan advices: I call editor template for Exercises as follows:

@Html.EditorFor(x => x.Lesson.Exercises)

and inside this EditorTemplate I call Editor Template for my ExerciseItems

@Html.EditorFor(x=>x.ExerciseItems)

this renders the following markup for UserValue property:

<input id="Lesson_Exercises_0__ExerciseItems_1__UserValue" name="Lesson.Exercises[0].ExerciseItems[1].UserValue" type="text" value="">

but it does not work also

niao
  • 4,972
  • 19
  • 66
  • 114

1 Answers1

2

Don't use the foreach. EditorTemplates already iterate over collections if you pass it a collection.

@model MyNamespace.ExerciseForPupil

@using (Html.BeginForm("ScoreExercise", "SharedLesson"))
{
    @Html.HiddenFor(x => x.Id)
    @Html.EditorFor(x =>  x.ExerciseItemsForPupil)
    <input type="submit" value="Submit"/>
}

A few things to note. You don't have to pass the template name, as the template name is already the same name as the item type. You don't have to use the Post formmethod, as that's the default. There is no name of the property for List so I just assumed it was the plural.

Your last class is also illegal, you would not specify it as a List like that.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • of course my last class is illegal. That was just my mistake. But thanks for pointing this out – niao Apr 28 '13 at 20:43
  • @PiotrPtak - You also have errors in ExerciseForPupil and Lesson, the last properties in each have no property name. The point is, I have to make assumptions about things if you don't supply valid code. Those assumptions may or may not be correct, so strive to provide code that actually works. – Erik Funkenbusch Apr 28 '13 at 20:47
  • I updated my question. I also get rid of foreach and pass the collections to my editor templates but it does not works also – niao Apr 28 '13 at 20:51
  • 1
    @PiotrPtak - your code still has errors, ExerciseForPupil's last property still has no property name. The problem with this "simplified" example is that your real code is probably doing something else that makes a difference, but since you're not including your real code we can only tell you what's wrong with your example code. By the way, do you have an `[HttpPost]` attribute on your ScoreExercise method? – Erik Funkenbusch Apr 28 '13 at 20:53
  • Yes I have [HttpPost] attribite on my ScoreExercise method – niao Apr 28 '13 at 20:56
  • @PiotrPtak - What is SharedLesson? Your code doesn't show that, other than in your BeginForm. – Erik Funkenbusch Apr 28 '13 at 20:57
  • Yep, it was not a good idea to simplify this code. I again updated my question – niao Apr 28 '13 at 21:01
  • @PiotrPtak - The problem here is that you are posting to a different model than you are rendering from. You render from some parent model that contains a Lesson, but you're posting to a method that accepts a single ExerciseItemForPupil. Your system is setup to post a collection of objects, not a single one. – Erik Funkenbusch Apr 28 '13 at 21:06
  • yes I know, because I just want to edit a single Exercise and not a whole Lesson which contains many Exercises. I thought that I can do this by placing multiple BeginForm forms. My BeginForm is places inside EditorTemplate for Exercise (of type ExerciseForPupil). My method also accepts this model. I thought this will be fine. – niao Apr 28 '13 at 21:13
  • @PiotrPtak - My mistake, I realized you are accepting the ExerciseForPupil not ExerciseItemForPupil (similar named items cause confusion) but that should work. – Erik Funkenbusch Apr 28 '13 at 21:41
  • well..it does not work. Maybe because the id and name attributes for my inputs stars with "Lesson"? – niao Apr 28 '13 at 21:48
  • @PiotrPtak - Like I said, the problem is that you are rendering from a different base model than you are posting to. The easiest solution is to change the model of your post action method to be whatever your actual base model class is (the view model that contains the Lesson object). Then you would just walk that down to get to the actually posted collections (the ones that you did not post will have no elements) – Erik Funkenbusch Apr 28 '13 at 21:52
  • @PiotrPtak - another option would be to make ExerciseForPupil a Partial view instead of an EditorTemplate, and pass the Exercises object to the Partial. That will cause the EditorTemplate to render from the correct base. A third option is to play with the Editor variables to remove the parents, but that's kind of hacky – Erik Funkenbusch Apr 28 '13 at 21:54
  • Is there a way to create a global template for `ICollcetion` etc.? – Shimmy Weitzhandler Mar 29 '15 at 01:10