4

My model is simple, one client can have many phone numbers :

I have represented this in Entity Framework

Generated client class is as below.

public partial class Client
{
    public Client()
    {
        this.PhoneNumbers = new HashSet<PhoneNumber>();
    }

    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
}

And now I need to create a view page for "create client". This page should have space to enter PhoneNumbers also (ex: By default there should be two text boxes to enter phone numbers)

<fieldset>
    <legend>Client</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.Name)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Name)
        @Html.ValidationMessageFor(model => model.Name)
    </div>

    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>

as the above "create view" we can easily give an space for "model.Name", because it is a simple property. But how can i do something similar for collection of phone numbers..??

I know that we can achieve this with ugly javascript code, but I would like to know the best easy and simple way, that we can use with ASP.NET MVC ... ?

hoang
  • 1,887
  • 1
  • 24
  • 34

2 Answers2

7

You have to do a few things:

First create a ViewModel that has the properties you need:

public class ClientViewModel
{
   public int Id {get;set;}
   public string Name {get;set;}
   public PhoneNumber PhoneNumber1 {get;set;}
   public PhoneNumber PhoneNumber2 {get;set;}
}

Change Create to return the ClientViewModel

[HttpGet]
public ActionResult Create()
{
   return View(new ClientViewModel());
}

Map the HttpPost to use the ClientViewModel and map the values to it:

[HttpPost]
public ActionResult Create(ClientViewModel clientViewModel)
{
   var client = new Client();
   client.Name = clientViewModel.Name;
   client.PhoneNumbers.Add(clientViewModel.PhoneNumber1);
   client.PhoneNumbers.Add(clientViewModel.PhoneNumber2);
   db.Clients.Add(client);
   db.SaveChanges();
   return RedirectToAction("Index", "Client");
}

Then, finally, modify your view:

<fieldset>
   <legend>Client</legend>

   <div class="editor-label">
      @Html.LabelFor(model => model.Name)
   </div>
   <div class="editor-field">
      @Html.EditorFor(model => model.Name)
      @Html.ValidationMessageFor(model => model.Name)
   </div>

   <div class="editor-label">
      @Html.LabelFor(model => model.PhoneNumber1.Number)
   </div>
   <div class="editor-field">
      @Html.EditorFor(model => model.PhoneNumber1.Number)
      @Html.ValidationMessageFor(model => model.PhoneNumber1.Number)
   </div>

   <div class="editor-label">
      @Html.LabelFor(model => model.PhoneNumber2.Number)
   </div>
   <div class="editor-field">
      @Html.EditorFor(model => model.PhoneNumber2.Number)
      @Html.ValidationMessageFor(model => model.PhoneNumber2.Number)
   </div>

   <p>
      <input type="submit" value="Create" />
   </p>
</fieldset>
SOfanatic
  • 5,523
  • 5
  • 36
  • 57
  • This solution brings me a better way of doing this. I didnt know about "View Models" until now. Thanks a lot for proposing this solution. – Janaka Priyadarshana Jul 16 '13 at 03:46
  • 1
    What if the number of phone numbers was not limited? – chris Jun 10 '15 at 08:54
  • 1
    @chris you can create a view model for 1 phone number and then create a editor template for it. Then propably using some js add the editor template to the page dynamically and post the collection to the controller. – SOfanatic Jun 10 '15 at 11:22
2

For the collection you can use something like this:

@for(int i = 0; i < Model.PhoneNumbers.Count; i++)
{
   <div class="editor-field">
        @Html.EditorFor(model => model.PhoneNumbers[i])
        @Html.ValidationMessageFor(model => model.PhoneNumbers[i])
    </div>
}
Oleksii Aza
  • 5,368
  • 28
  • 35