In an Asp.Net MVC 3 application, how can I collect complicated child objects?

asp.net asp.net-mvc-3 collections entity entity-framework

Question

A model and all of its collections of child objects should be able to be updated in the same view. The examples http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx and http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ have been given to me.

As an example, I have an object called Consultant that has a group of "WorkExperiences." A model for Entity Framework has all of these. Simple attributes of the Consultant object in the view are no issue, however I'm having trouble getting a textbox to appear for the collection. It didn't work when I attempted to follow the examples in the links above. The issue is that the model in those situations is merely a list (not an object with a child list property). Additionally, the model is an EF model once again. And strangely, it doesn't appear to operate as well as in those situations.

To keep things simple, I attempted to follow Phil Haacks approach and just get the View to display the textbox:

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

I made an attempt to add a fresh WorkExperience object to the ViewModel's controller:

    public ActionResult Create(int id)
    {
        Consultant consultant = _repository.GetConsultant(id);
        DetailsViewModel vm = new DetailsViewModel();
        vm.WorkExperiences = consultant.WorkExperiences.ToList();
        vm.WorkExperiences.Add(new WorkExperience());           
        return View(vm);
    }

However, the WorkExperience Name property's empty textbox is not shown in the view. However, if I create a separate View only for adding a new WorkExperience object and use a fresh, empty WorkExperience object as the model, everything works as it should:

@Html.EditorFor(model => model.Name)

I now have a textbox that is empty, and I may save the new item. But why, using collections in accordance with the examples in the links above, can't I accomplish this in the same view as the Consultant object?

By the way, this is kind of a follow-up query to an earlier one that led to the aforementioned links, but I was never able to get a conclusive answer to. If further information is required, see that query at In an MVC 3 application, how can I create views for object properties?.

UPDATE:

Here is an update with a View and an EditorTemplate in response to the responses and comments below:

The Opinion

@model Consultants.ViewModels.DetailsViewModel

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Add work experience", "CreateWorkExperience", new { id = ViewBag.Consultant.Id })
</p>
<table>
    <tr>
        <th></th>
        <th>
            Name
        </th>

    </tr>

@foreach (var item in Model.WorkExperiences) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
            @Html.ActionLink("Details", "Details", new { id = item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id = item.Id })
        </td>
        <td>
            @item.Name
        </td>

    </tr>

}

</table>

@for (int i = 0; i < Model. WorkExperiences.Count; i++)
{
    Html.EditorFor(m => m. WorkExperiences[i]);
}

(Please note that this is not how I will really build it; all I am aiming for at the moment is to have the WorkExperience object appear as an empty textbox to fill up, and to have the ability to add and remove such textboxes as in Phil Haack's and Steven Sanderson's examples.)

Editorial Template:

@model Consultants.Models.WorkExperience


@Html.TextBoxFor(m => m.Name);

In Phil Haack's example project, which I downloaded to check out, this stuff with the EditorTemplate works just great, but here, with the EF model or whatever the issue is, I don't receive any textbox at all. The table in the view is only there as a test, since I am able to see the rows for WorkExperiences there regardless of whether I add an empty WorkExperience object or complete up its parameters. Again, there is no textbox

1
6
5/23/2017 10:29:40 AM

Accepted Answer

For example, I have an object Consultant, that has a collection of "WorkExperiences". All this is in an Entity Framework model.

Introduce view models instead of using your domain models in the view; that's the first thing you should do to enhance.

So with that stated, let's get to the templates. Consequently, you won't need to create any loops in your views.

So, here is how your perspective may appear:

@model Consultants.ViewModels.DetailsViewModel

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Add work experience", "CreateWorkExperience", new { id = ViewBag.Consultant.Id })
</p>
<table>
    <tr>
        <th></th>
        <th>
            Name
        </th>
    </tr>
    @Html.DisplayFor(x => x.WorkExperiences)
</table>

@Html.EditorFor(x.WorkExperiences)

As you can see, we're using both a display template and an editing template. Now let's define them.

Display model (~/Views/Shared/DisplayTemplates/WorkExperience.cshtml ):

@model AppName.Models.WorkExperience
<tr>
    <td>
        @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
        @Html.ActionLink("Details", "Details", new { id = Model.Id }) |
        @Html.ActionLink("Delete", "Delete", new { id = Model.Id })
    </td>
    <td>
        @Model.Name
    </td>
</tr>

Editor skeleton (~/Views/Shared/EditorTemplates/WorkExperience.cshtml ):

@model AppName.Models.WorkExperience
@Html.TextBoxFor(x => x.SomePropertyOfTheWorkExperienceModelYouWantToEdit)
...

The naming convention is crucial in this situation. The name of the template should correspond to the name of the collection item's type. Therefore, if you have a property in your view model, for instance

public IEnumerable<Foo> { get; set; }

the appropriate template has to be calledFoo.cshtml and need to be situated in~/Views/Shared/DisplayTemplates or ~/Views/Shared/EditorTemplates based on what it does.

Therefore, as you can see, we have eliminated the unpleasant loops. Now, in addition to having tidy views, you also have input fields with the proper names so you can bind their values in the post action.

12
2/21/2011 8:27:29 AM

Popular Answer

Probably the simplest method to do this is to establish aWorkExperienceList class with inheritanceList<WorkExperience> (or List<string> , if that's what they are), after which you should make a unique template for yourWorkExperienceList You may then streamline your view code to@Html.EditorFor(Model) , with ASP.NET MVC handling the rest for you.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow