Sunday 14 September 2014

MULTIPLE VIEWMODELS IN A SINGLE MVC VIEW

This post shows how to use multiple models in a single view. The models can be used in separated forms or together in one form. You could use each child model with a separate ajax post if required. Only properties in the parent class affect the child models, but a property in a child model does not effect the Model State of the other child models. In this way multiple models can be used and also validation for each model.

How the ViewModels are implemented:
The parent model class contains all child classes. Each class is another reusable model. In this way, the validation and ModelState can be used.
namespace MultipleModelsDemo.ViewModels
{
    public class IndexViewModel
    {
        public string HeaderText { get; set; }
 
        public TestOneModel TestOne { get; set; }
 
        public TestTwoModel TestTwo { get; set; }
    }
}

A child model used for the first form.
1
2
3
4
5
6
7
8
9
10
11
12
13
using System.ComponentModel.DataAnnotations;
 
namespace MultipleModelsDemo.ViewModels
{
    public class TestOneModel
    {
        [Required]
        public string PropertyOne { get; set; }
 
        [Required]
        public string PropertyTwo { get; set; }
    }
}
A second child model used for the second form.
1
2
3
4
5
6
7
8
9
10
11
12
13
using System.ComponentModel.DataAnnotations;
 
namespace MultipleModelsDemo.ViewModels
{
    public class TestTwoModel
    {
        [Required]
        public string PropertyThree { get; set; }
 
        [Required]
        public string PropertyFour { get; set; }
    }
}
In the controller a single action method is used to recieve the form posts. The action method MyEditActionOne uses the Index model. In this action method, the ModelState.IsValid property can be checked. Due to the model structure, separate forms can be sent for each child model, or the whole model as one. Note: Any properties defined in a parent class muss be valid if set as required.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Web.Mvc;
using MultipleModelsDemo.ViewModels;
 
namespace MultipleModelsDemo.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new IndexViewModel();
            model.HeaderText = "Strongly typed model used here, no view bag";
            return View(model);
        }
 
        public ActionResult MyEditActionOne(IndexViewModel model)
        {
            if (ModelState.IsValid)
            {
                return View("Index", model);
            }
 
            throw new Exception("My Model state is not valid");
        }
    }
}
The html uses razor and can send either a form with just the first child model, or the second child model or the whole model as one. Only what is required for each form is validated. This is due to the model structure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@model MultipleModelsDemo.ViewModels.IndexViewModel
 
<div>
    @using (Html.BeginForm("MyEditActionOne", "Home"))
    {
        <div>
            <h4>Send Model One</h4>
            <fieldset>
                @Html.ValidationSummary(true, "ValidationSummary")
                <ol>
 
                    <li>
                        @Html.LabelFor(m => m.TestOne.PropertyOne)
                        @Html.TextBoxFor(m => m.TestOne.PropertyOne)
                        @Html.ValidationMessageFor(m => m.TestOne.PropertyOne)
                    </li>
                    <li>
                        @Html.LabelFor(m => m.TestOne.PropertyTwo)
                        @Html.TextBoxFor(m => m.TestOne.PropertyTwo)
                        @Html.ValidationMessageFor(m => m.TestOne.PropertyTwo)
                    </li>
                </ol>
            </fieldset>
             
            <input class="btn" type="submit" value="Send" />
        </div>
    }
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<div>
    @using (Html.BeginForm("MyEditActionOne", "Home"))
    {
        <div>
            <h4>Send both Models</h4>
            <fieldset>
                @Html.ValidationSummary(true, "ValidationSummary")
                <ol>
 
                    <li>
                        @Html.LabelFor(m => m.TestOne.PropertyOne)
                        @Html.TextBoxFor(m => m.TestOne.PropertyOne)
                        @Html.ValidationMessageFor(m => m.TestOne.PropertyOne)
                    </li>
                    <li>
                        @Html.LabelFor(m => m.TestOne.PropertyTwo)
                        @Html.TextBoxFor(m => m.TestOne.PropertyTwo)
                        @Html.ValidationMessageFor(m => m.TestOne.PropertyTwo)
                    </li>
                    <li>
                        @Html.LabelFor(m => m.TestTwo.PropertyThree)
                        @Html.TextBoxFor(m => m.TestTwo.PropertyThree)
                        @Html.ValidationMessageFor(m => m.TestTwo.PropertyThree)
                    </li>
                    <li>
                        @Html.LabelFor(m => m.TestTwo.PropertyFour)
                        @Html.TextBoxFor(m => m.TestTwo.PropertyFour)
                        @Html.ValidationMessageFor(m => m.TestTwo.PropertyFour)
                    </li>
                </ol>
            </fieldset>
 
            <input class="btn" type="submit" value="Send" />
        </div>
    }
</div>
As shown, it is very easy to implement multiple strongly typed models in a single view. Using this structure it would also be possible to implement a dynamic view with multiple forms or data.
Links: