Nested Collection Models in MVC to Add Multiple Phone Numbers - Part 1

In this article series, you will learn how to create MVC application that will allow adding multiple phone numbers using Nested Model Concept. User will be able to add or remove (zero or more phone numbers, I need minimum two phone numbers for each employee) phone numbers for any single employee. Also in later posts, I will show you how to List, Edit, Delete record(s) in such nested (relational) models concept. Here is the first look of the concept.


Now, I will go step by step so that you can understand what is going on.

Step 1: Database

First thing first you need is database tables with proper relationship. The main concept is, there will be none or more Phone Numbers in ‘Phone’ table for each Employee in ‘Employee’ table.


Note, we have navigation properties in both tables and ‘EmployeeId’ in Phone table for each phone numbers.

You will find a property ‘DeletePhone’ in Phone table, this property will be used as a flag to delete contact number by controller method when user marked for deletion on View. Actually, this field is not required in database table when you have ViewModel class, and this is a quick demo so rather creating a class I will prefer adding property in database table.

I will share the script file to generate this database at the end of this article. I am using Entity Data Model to generate model classes and DbContext. Once you done with this, move to next step.

Step 2: Prepopulating Collection (Phone Number input boxes)

In the very first animated image, you can see page gets loaded with two ‘Phone Number’ input boxes. To do this I need to update ‘Employee’ model (this model is generated by Entity Data Model) with following highlighted method.

public partial class Employee
{
    public Employee()
    {
        this.Phones = new HashSet<Phone>();
    }
   
    public int EmployeeId { get; set; }
    public string Name { get; set; }
    public string Salary { get; set; }
   
    public virtual ICollection<Phone> Phones { get; set; }

    internal void CreatePhoneNumbers(int count = 1)
    {
        for (int i = 0; i < count; i++) {
            Phones.Add(new Phone());
        }
    }
}

This highlighted method ‘CreatePhoneNumbers’ will take a parameter and add that collection to ‘Phones’ model property.

Now, let’s add a new controller ‘Employee’ and an action method ‘Add’ and then we will make a call to above highlighted method, here you go.

public ActionResult New()
{
    var employee = new Employee();
    employee.CreatePhoneNumbers(2);
    return View(employee);
}

I just created the object of Employee model class and making a call to ‘CreatePhoneNumbers’ method with 2 as a parameter, this will help in generating two input boxes on the View page. Add the View Page for this Action method.

Step 3: Creating View

Creating View for above employee model is also simple. Let’s update the view page you added in above step by following code.

New.cshtml

@{
    ViewBag.Title = "New";
}

<h2>Add New Employee</h2>

@using (Html.BeginForm()){
    @Html.AntiForgeryToken()

    @Html.EditorForModel()
    <p>
    <button type="submit">Create Employee</button>
    </p>
}

If you run this view here, you will see following output.


So, I will call this view page template-less. You can see I am using EditorForModel(), this will look at the type of model, then go looking for available templates in EditorTemplates folder for a file named Employee.cshtml that we don’t have. Let’s add a new View page at the location Views|Employee|EditorTemplates|Employee.cshtml and add following code.

Employee.cshtml

@model NestedModelsMvc.Models.Employee

@Html.HiddenFor(x => x.EmployeeId)

<p>
  <label>Name</label>
  @Html.TextBoxFor(x => x.Name)
</p>

<p>
  <label>Salary</label>
  @Html.TextBoxFor(x => x.Salary)
</p>

<div id="phoneNumbers">
  @Html.EditorFor(x => x.Phones)
</div>

Now, again inside Employee.cshtml, we have a EditorFor(). This will again look at the type of model, then go looking for available templates in EditorTemplates folder for a file named Phone.cshtml that we don’t have. Let’s add this too.

Phone.cshtml

@model NestedModelsMvc.Models.Phone

<div class="phoneNumber">
  <p>
    <label>Phone Number</label>
    @Html.TextBoxFor(x => x.PhoneNumber)
    @Html.HiddenFor(x => x.DeletePhone, new { @class = "mark-for-delete" })
  </p>
</div>

Now, let me show you the view files so that you don’t confuse with the view file placements.


Now we are ready to run the New.cshtml file to look at the generated UI.


Great Start! Let’s look at the generated HTML markups.


In above HTML markup, you can see two div tags that has class="phoneNumber" and inside that we have two input fields.

First field which is generated by TextBoxFor(x => x.PhoneNumber) has type="text" where user will enter phone number. And second field which is generated by HiddenFor(x => x.DeletePhone, new { @class = "mark-for-delete" }) has type="hidden" which will work as flag whether to delete or not.

Notice, we called EditorFor(x => x.Phones) in Employee.cshtml and it looped through our phone numbers and created two nested editors and each editor has format defied in Phone.cshtml and see how smartly id and name attributes filled up with index based words.

You can also notice the value attribute is blank, this will be filled with value="True" when user will mark for deletion. So, if value is blank we don’t need to execute delete query, you will see it later.

Step 4: Deleting Phone Number

You will learn this step in Part 2.

Hope this helps.

Comments

  1. This is nice concept but i want to implement its on asp.net without use MVC
    Than please give me solution

    ReplyDelete
  2. Please provide source code. At the end of this article the mentioned url is invalid.

    ReplyDelete
  3. what if am using a dropdownlist instead of the phone textbox, how to populate the dropdownlist in the phone.cshtml

    ReplyDelete
  4. This is a wonderful article. My data is in another project which is reference but when i add the internal void CreatePhonenumbers it is not recognized as being a part of my class. is there an issue if the Model is in a separate project?

    ReplyDelete
  5. Great, simple explanation... Thank you!

    ReplyDelete
  6. hi
    i have small doubt

    saved the same data same two tables and updated the data in one
    table using mvc
    could please explain

    ReplyDelete

Post a Comment

Popular posts from this blog

Migrating database from ASP.NET Identity to ASP.NET Core Identity

Customize User's Profile in ASP.NET Identity System