Thursday, 14 May 2015

Custom Editor Template for a complex type in MVC

Hi,

Here I am going to demonstrate, How we can create a custom editor template for a complex type.

Let's take a scenario, We are building a application where a student can choose subject he/she wants to learn. Student and Subjects information are available in database. Here is corresponding model classes for Student and Subject. This is just an example.
 public class Student
    {
        public string Name { get; set; }
        public string Class { get; set; }
    }

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

    }

And our UI should be something like this:
 and output should be like this.

There are multiple ways of doing this and easy way to achieve this is using custom editor template. Here I have two model classes "Student" and "Subject" and I can use this model classes as it is, to bind with view. So what to do. Here is the way, We need to create a ViewModel (ViewModel is required when we have more than one model classes for View, as we need in this example.).
Here I am gonna define ViewModel:

    public class StudentViewModel
    {
        public Student Student { get; set; }
        public List<SubjectsOpted> SubjectsOptedFor { get; set; }
    }

    public class SubjectsOpted
    {
        public Subject Subject { get; set; }
        public bool OptedIn { get; set; }
    }


What I did here?,
First, Created a class which has property of Subject class type and a bool type property. Actually purpose of doing this is, "I need custom editor template for this complex type which basically will help us to achieve the kind of UI we need.
Second, Created a class called StudentViewModel which will be used for binding for View.
Third, Will create custom editor for complex type SubjectOpted.

and here are the steps to create Custom Editor Template for complex type:
1. Add a folder called "EditorTemplates"
2. Create a cshtml file with the name of complex type, in this case it will be SubjectsOpted.cshtml.
3. Add code for this view:
    @model MVCEditorTemplatesExample.Models.SubjectsOpted
        @Html.CheckBoxFor(m => m.OptedIn, new { @checked = "checked" })
        @Html.HiddenFor(m => m.Subject.Id)
        @Html.HiddenFor(m => m.Subject.Name)
        @Html.DisplayFor(m => m.Subject.Name) <br />
4. Yeah, we are done with our custom Editor template for complex type.That's it. :)

Hold on, I still need to show you, how to use this template. Create a view and add below code,
@model MVCEditorTemplatesExample.Models.StudentViewModel
<!DOCTYPE html>
<html>
<head></head>
<body>
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryval")

    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()
        <div class="form-horizontal">
            <h4>Student</h4>
            <hr />
            @Html.ValidationSummary(true)
            <div class="form-group">
                @Html.LabelFor(model => model.Student.Name, new { @class = "control-label col-md-2" }):
                @Html.EditorFor(model => model.Student.Name)
                @Html.ValidationMessageFor(model => model.Student.Name)
            </div>
            <br />
            <div class="form-group">
                @Html.LabelFor(model => model.Student.Class, new { @class = "control-label col-md-2" }):
                @Html.EditorFor(model => model.Student.Class)
                @Html.ValidationMessageFor(model => model.Student.Class)
            </div>
            <br />
            <div>
                <b>Choose Subjects:</b><br />
                @Html.EditorFor(model => model.SubjectsOptedFor)
            </div>
            <br />
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
    }
    <br />
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
</body>
</html>

Take a look of highlighted code here, this is how we need to call editor template. too tough isn't it :). Based on complex type and name we used for template, MVC is smart enough to decide what editor should be called.



Let's also take a look of Post method, How view model data will get posted back. Mind it, it will be very tough task to do. Here is the code:
        [HttpPost]
        public ActionResult Create(StudentViewModel std)
        {
            try
            {
                std.SubjectsOptedFor.Remove(std.SubjectsOptedFor.FirstOrDefault(itm => itm.OptedIn == false));
                return View("View", std);
            }
            catch
            {
                return View();
            }
        }

:) Thanks to MVC model binder, who is smart enough to bind posted data with viewmodel which we created.

At last, in case if you want to see the code of view which I wrote to render the data (as shown in second UI at top), here it is.
@model MVCEditorTemplatesExample.Models.StudentViewModel
<html>
<head></head>
<body>
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        <tr>
            <th></th>
        </tr>
        <tr>
            <td>
                <b>Name:</b> @Html.Label(Model.Student.Name) <br/>
                <b>Class:</b> @Html.Label(Model.Student.Class) <br/>
                <b>Subject Opted for:</b>
                <table>
                    @foreach (var subject in Model.SubjectsOptedFor)
                    {
                        <tr>
                            <td>
                                @if (subject.OptedIn) { 
                                    @Html.Label(subject.Subject.Name)} &nbsp;
                            </td>
                        </tr>
                    }
                </table>
            </td>
        </tr>
    </table>
</body>
</html>

That's it.

Thank You for reading. Don't forget to like it and feel free to give your feedback/comment.

No comments:

Post a Comment