One-to-many relationship in the create action method

I'm currently working on a project that requires add multiple addresses for one client. I have three models: here is a link to my class diagram: http://sdrv.ms/1fWioA2 

Person model: 

public class Person
{   
    public Person()
    {
        this.Adresses = new HashSet<Address>();
    }
 
    public int PersonID { get; set; }
 
    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; }
 
    [Required(ErrorMessage = "Email Address is required")]
    [DisplayName("Email Address")]
    //[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
    //ErrorMessage = "Email is is not valid.")]
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }
 
    public string Mobile { get; set; }
    public string Phone { get; set; }
    public string Fax { get; set; }
 
    public virtual ICollection<Address> Adresses { get; set; }
}

address: 

public class Address
{
    [HiddenInput(DisplayValue = false)]
    public int ID { get; set; }
    public string Street { get; set; }
    public string Building  { get; set; }
 
    public int PersonID { get; set; }
    public int CityID { get; set; }
 
    public virtual City City { get; set; }
    public virtual Person Person { get; set; }
}
I created modelview to bind to my Create view:
 
public class PersonViewModel
{
    public Person Person { get; set; }
    public ICollection<Address> Adresses { get; set; }
}

My question is how to bind the viewmodel to the create view in order to be able to save multiple addresses for the same Person?

Last updated:12/6/2014 5:42:08 AM

2 Answers

Anonymous User
Anonymous User

For this purpose, create an editor template in /Shared/Editor called Address.cshtml: 

@model Address 
<div class='address'>
@Html.HiddenFor(model => model.ID)
@Html.HiddenFor(model => model.PersonID)
@Html.HiddenFor(model => model.CityID)
@Html.TextBoxFor(model => model.Street) <br />
@Html.TextBoxFor(model => model.Building  ) <br />
</div>

Now, in your View, all you need to do is this: 

@Html.EditorFor(v => v.Addresses)

This will create form fields like this: 

<input type='hidden'name='address[0].Id' />
<input type='hidden'name='address[0].PersonId' />
<input type='hidden'name='address[0].CityId' />
<input type='text'name='address[0].Street' />
<input type='text'name='address[0].Building' />
<input type='hidden'name='address[1].Id' />
<input type='hidden'name='address[1].PersonId' />
<input type='hidden'name='address[1].CityId' />
<input type='text'name='address[1].Street' />
<input type='text'name='address[1].Building' />

Anonymous User
Anonymous User

I recommend you to use client side approach using knockout as example: Server side: 

 public class HomeController : Controller{
    //
    // GET: /Home/
 
    public ActionResult Index()
    {
        var model = new
        {
            person = new { firstName = '123', secondName = '' },
            addresses = new dynamic[1] { new { street = '', building = '', selectedCityId = '' } },
            cities = new dynamic[2] { new { cityName = 'Kiev', cityId = '1' }, new { cityName = 'Moskow', cityId = '2' } }
 
        };
        return View(model);
    }
 
    public ActionResult Save(YourType model)
    {
        .....
    }
 
}

And client side: 

@{
Layout = null;
}
@using Newtonsoft.Json;
 
<!DOCTYPE html>
 
<html>
<head>
<meta name='viewport' content='width=device-width' />
<title>Index</title>
</head>
<body>
<div style='margin-left:100px'>
    <p>First name:</p>
    <input data-bind='value:person.firstName' /> 
    <p>Second name:</p>
    <input data-bind='value:person.secondName' />
    <h3>Addresses</h3>
    <div data-bind='foreach:addresses'>
        <p>
            Street
        </p>
        <input data-bind='value:street' />
        <p>
            Building
        </p>
        <input data-bind='value:building' />
        <p>
            City
        </p>
        <select data-bind='options: $parent.cities, optionsText:
           'cityName',optionsValue:'cityId',
              value: selectedCityId , optionsCaption: 'Choose...''></select>
        <div>
            <div style='float:left;'>
                <button data-bind='click:$parent.add'>
                    Add
                </button>
            </div>
            <div style='float:left;'>
                <button data-bind='click: $parent.remove'>
                    Delete
                </button>
            </div>
        </div>
        <div style='clear:both'></div>
    </div>
    <button style='width:100px'data-bind='click:save'>
        Save
    </button>
 
 </div>
 <script src='~/Scripts/jquery-1.8.2.js'></script>
 <script src='~/Scripts/knockout-3.0.0.js'></script>
 <script src='~/Scripts/knockout.mapping-latest.js'></script>
 <script type='text/javascript'>
    //This create javascript object from your model
    var personModel=@Html.Raw(JsonConvert.SerializeObject(Model))
    //Parse javascript object to knockout viewModel
    var viewModel=ko.mapping.fromJS(personModel);
    // Create new address
    function Address(){
        this.selectedCityId=ko.observable('');
        this.street=ko.observable('');
        this.building=ko.observable('');
    }
    //Add new address
    viewModel.add=function(){
        viewModel.addresses.push(new Address()); 
    };
    //Remove address
    viewModel.remove =function(item){
        if(viewModel.addresses().length>1){
            viewModel.addresses.remove(item)
        }        
    };
    //save model
    viewModel.save=function(){
        unmapped=ko.mapping.toJSON(viewModel);
        $.post('/home/save',unmapped)
    };
    ko.applyBindings(viewModel);
</script>
</body>
</html>

Answer