MVC - Create object and related objects in one go

2019-01-24 15:16发布

问题:

I want to create a parent object with child/related objects in the same view. An example would be: create one Father (with some name) along with all his sons (with their names). I have created a view model:

public class FatherViewModel {
  public Father father {get; set;} // has 1 property Name
  public List<Son> {get; set;} // has 1 property Name
}

My question is, how do I get the list of Sons back from the view when the post is performed? I have tried using HiddenFor for each Son id, but no matter what, the list is empty when returned to the controller.

UPDATE:

I tried the Editor Template example by Shyju described below, but my editor is never called. I have 1 object:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? FatherId { get; set; }
    public virtual ICollection<Person> Children { get; set; }
}

I did this:

  1. Scaffolded a full controller for Person with index, create, edit...
  2. Created EditorTemplates folder in Views->Person
  3. Created Person.cshtml:

    @model TestEditorTemplate.Models.Person <div> <h4>Child</h4> @Html.TextBoxFor(s => s.Name) @Html.HiddenFor(s => s.Id) </div>

  4. Added @Html.EditorFor(m => m.Children) to Create.cshtml

Questions:

  1. How can @Html.EditorFor(m => m.Children)possibly work with the editor template when m.Children is a collection of Person and not a single Person?
  2. I want to create (not edit) a father including children at the same time. That means that I have no Ids to pass to the Create view to start with. How can this work? From the example by Shyju, the Ids are already created beforehand?? Or did I just misunderstand the example?

回答1:

You can use EditorTemplates to handle this. Here is a working sample.

So i have a viewmodel to represent the father-child relationship

public class PersonVM
{
    public int Id { set; get; }
    public string Name { set; get; }
    public int? ParentId { set; get; }
    public List<PersonVM> Childs { set; get; }
}

And in my GET action method, i create an object of my view model and load the Father -childs data to it.

public ActionResult EditorTmp(int id = 1)
{
    //Hard coded for demo, you may replace with actual DB values
    var person = new PersonVM {Id = 1, Name = "Mike"};
    person.Childs = new List<PersonVM>
    {
        new PersonVM {Id = 2, Name = "Scott", ParentId = 11},
        new PersonVM {Id = 2, Name = "Gavin", ParentId = 12}
    };
    return View(person);
}

Now i will create an EditorTemplate. To do that, Go to your Views folder, and Create a directory called EditorTemplates under the directory which has same name as the controller, and add a view with name PersonVM.cshtml

Now, go to this view and add the below code.

@model ReplaceWithYourNameSpaceNameHere.PersonVM
<div>
    <h4>Childs </h4>
    @Html.TextBoxFor(s => s.Name)
    @Html.HiddenFor(s => s.Id)
</div>

Now let's go back to our main view. We need to make this view strongly typed to our original PersonVM. We will use the EditorFor html helper method in this view to call our editor template

@model ReplaceWithYourNameSpaceNameHere.PersonVM
@using (Html.BeginForm())
{
    <div>
        @Html.TextBoxFor(s => s.Name)
        @Html.HiddenFor(s => s.Id)
    </div>
    @Html.EditorFor(s=>s.Childs)
   <input type="submit"/>
}

Now have an HttpPost method in the controller to handle the form posting

[HttpPost]
public ActionResult EditorTmp(PersonVM model)
{
    int fatherId = model.Id;
    foreach (var person in model.Childs)
    {
        var id=person.Id;
        var name = person.Name;
    }
    // to do  : Save ,then Redirect (PRG pattern)
    return View(model);
}

Now, If you put a break point in your HttpPost action method, you can see the Id's of childs are passed to this action method.

One important thing to remember is, Your Editor Template view's name should be same as the type you are binding to it.