Saturday, 15 March 2014

Submitting form with partial view in ASP.NET MVC

In this tutorial we want to submit form with partial model.

Download full project here

We are going to have ClientVM that will have partial view with Address model. 
Create new MVC 4 project, choose internet project and Razor.

Create models that we will use in this project.

Models
public class Client
    {
        public Client()
        { 
        }

        public Client(string firstName, string lastName, Guid addressId)
        {
            this.address = new Address();
            this.clientId = Guid.NewGuid();
            this.firstName = firstName;
            this.lastName = lastName;
            this.addressId = addressId;
        }

        public Guid clientId { get; set; }
        public string firstName { get; set; }
        public string lastName { get; set; }
        public Guid addressId { get; set; }
        public virtual Address address { get; set; }
    }

public class Address
{
    public Address()
    { 

    }

    public Address(string houseNumber, string streetName)
    {
    this.addressId = Guid.NewGuid();
    this.houseNumber = houseNumber;
    this.streetName = streetName;
    }

    public Guid addressId { get; set; }
    public string houseNumber { get; set; }
    public string streetName { get; set; }
}

View Models

 public class ClientVM
    {
        public ClientVM()
        { 
            
        }

        public ClientVM(Client client, Address address)
        {
            this.clientId = Guid.NewGuid();
            this.firstName = client.firstName;
            this.lastName = client.lastName;
            this.addressId = client.addressId;
            this.addressPartial = address;
        }

        public Guid clientId { get; set; }
        public string firstName { get; set; }
        public string lastName { get; set; }
        public Guid addressId { get; set; }
        public Address addressPartial { get; set; }

    }


In Home controller we will create ActionResult ClientInfo

public ActionResult ClientInfo()
        {
            // Normally we would get Client from database. For test we use constructors to define new     entries.

            Address address = new Address("25 A", "London street");
            Client client = new Client("Adam", "Bielecki", address.addressId);

            ClientVM viewModel = new ClientVM(client, address);

            return View(viewModel);
        }

and the same method to submit the form:

[HttpPost]
        public ActionResult ClientInfo(ClientVM viewModel)
        {
            return View(viewModel);
        }

In _Layout_cs change 2nd link for menu.

<li>@Html.ActionLink("Submit Partial Model", "ClientInfo", "Home")</li>

so menu tabs should look like that:

<ul id="menu">
                            <li>@Html.ActionLink("Home", "Index", "Home")</li>
                            <li>@Html.ActionLink("Submit Partial Model", "ClientInfo", "Home")</li>
                            <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                        </ul>

Now we have to create 1 view for ClientInfo and 1 partial view for Address.
Compile your project (Shift+Ctrl+B), right click ClientInfo in Home Controller and Add View.

Check Create a strongly-type view, choose ClientVM and scaffold template : Edit.





Right click on shared folder add view and call it _Address. Use the same setting as before, but this time select Address as your model class and check box "Create as a partial view".

Remove form from _Address partial view so it should like that:



@model PartialViewModelSubmission.Models.Address

<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<h2>Address</h2>

    <fieldset>
        <legend>Address</legend>

        @Html.HiddenFor(model => model.addressId)

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

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

      
    </fieldset>


Run the application, click Submit Partial Model and submit the form. 
As you can see we get error as Address have not been submitted.



 The reason why it is happening is due to binding issue. Let's fix it.

We will have to create partial class for Address. In Address.cs add this partial class:


public partial class AddressBase
{
   public virtual Address partialAddress { get; set; } 
}

Using virtual type we are going to lazy load Address.

 Modify ClientVM so it looks like that:




public class ClientVM : AddressBase
    {
        public ClientVM()
        { 
            
        }

        public ClientVM(Client client, Address address)
        {
            this.clientId = Guid.NewGuid();
            this.firstName = client.firstName;
            this.lastName = client.lastName;
            //this.addressId = client.addressId;
            //this.addressPartial = address;
            this.partialAddress = address;
            
        }

        public Guid clientId { get; set; }
        public string firstName { get; set; }
        public string lastName { get; set; }
        // public Guid addressId { get; set; }
        // public Address addressPartial { get; set; }
    
}

ClientVM inherit from AddressBase partial class so we can assign address to base.
            this.partialAddress = address;

This is all you have to do for classes, we have to amend views a bit as well.

In ClientInfo we are going to pass ClientVM model to address so change it to :
@Html.Partial("_Address", Model)

In _Address view change model to AddressBase and update scaffolding so it should look like that:

@model PartialViewModelSubmission.Models.AddressBase

<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<h2>Address</h2>

    <fieldset>
        <legend>Address</legend>

        @Html.HiddenFor(model => model.partialAddress.addressId)

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

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

      
    </fieldset>
Now run the application again and if you followed everything correctly you should be able to see Address model when you debug.