To get started with the development, we have seen the post Introduction to HTML5 Development Using VS 2010, HTML5, jQuery Mobile and ASP.NET MVC 4.
See http://bit.ly/HTML5JQuery for more information
In an enterprise application development scenario, we might need to aggregate some of the models/data sources and present it as a whole to the controller. For simplicity, let us take a case where we need to enter a new student Name, Grade, and the State as shown in Fig 1.
The Grades are A, B, and C. But it might make much sense if we can show these grades as First Class, Second Class, Third Class instead of A,B, and C in the UI. So in the UI, we need to show First Class, Second Class, Third Class but while saving the data we need to save the corresponding values in the DB. Same holds for the state as well. When the user clicks on state dropdown selection icon, the states list pops up and displays all the states. When a state is selected, it is shown in the UI but when saved to the DB, the code names are saved like CA for California, as shown in figure 2.
Fig 1: Form for entering new student information
Fig 2: When clicked on the State drop down list, the above screen pops up.
The Magic of ViewModel: ViewModels allow you to shape multiple entities from one or more data models or sources into a single object, optimized for consumption and rendering by the view. The below figure 3 illustrates the concept of a ViewModel:
Fig 3: StudentViewModel aggregating the different models and data sources and supplying the information to the view as a single entity.
The purpose of a ViewModel is for the view to have a single object to render, alleviating the need for UI logic code in the view that would otherwise be necessary. This means the only responsibility, or concern, of the view is to render that single ViewModel object, aiding in a cleaner separation of concerns (SoC). Concerns are distinct aspects of the application that have a particular purpose (i.e., concern), and keeping these aspects apart means your application is more organized, and the code more focused. Putting data manipulation code in its own location away from the view and controller, enforces SoC. Using ViewModels in MVC leads to more easily maintainable and testable code.
Let us create our data sources. The first one is the grade and the code is as follows. The Grades
class is a simple Dictionary
object containing two type parameters of type string
. The class also contains the definitions for all the members in the Dictionary
(i.e., the grades data). The only property in the Grades
class is the GradeSelectList
, which is an object that HTML Helpers use with to render an HTML <select>
element that displays a listing of grades. The type Dictionary<string, string>
in the GradeSelectList
property maps to the grades abbreviation then grade name, respectively.
public class Grades
{
public static SelectList GradeSelectList
{
get
{
return new SelectList(GradeDictionary, "Value", "Key");
}
}
public static readonly IDictionary<string, string>
GradeDictionary = new Dictionary<string, string>
{
{"Choose…",""}
, { "First Class", "A" }
, { "Second Class", "B" }
, { "Third Class", "c" }
};
}
The same login for the states. The code for StatesDictionary
is as follows:
public class StatesDictionary
{
public static SelectList StateSelectList
{
get { return new SelectList(StateDictionary, "Value", "Key"); }
}
public static readonly IDictionary<string, string>
StateDictionary = new Dictionary<string, string> {
{"Choose…",""}
, { "Alabama", "AL" }
, { "Alaska", "AK" }
, { "Arizona", "AZ" }
, { "Arkansas", "AR" }
, { "California", "CA" }
, { "Colorado", "CO" }
, { "Connecticut", "CT" }
, { "Delaware", "DE" }
, { "District of Columbia", "DC" }
, { "Florida", "FL" }
, { "Georgia", "GA" }
, { "Hawaii", "HI" }
, { "Idaho", "ID" }
, { "Illinois", "IL" }
, { "Indiana", "IN" }
, { "Iowa", "IA" }
, { "Kansas", "KS" }
, { "Kentucky", "KY" }
, { "Louisiana", "LA" }
, { "Maine", "ME" }
, { "Maryland", "MD" }
, { "Massachusetts", "MA" }
, { "Michigan", "MI" }
, { "Minnesota", "MN" }
, { "Mississippi", "MS" }
, { "Missouri", "MO" }
, { "Montana", "MT" }
, { "Nebraska", "NE" }
, { "Nevada", "NV" }
, { "New Hampshire", "NH" }
, { "New Jersey", "NJ" }
, { "New Mexico", "NM" }
, { "New York", "NY" }
, { "North Carolina", "NC" }
, { "North Dakota", "ND" }
, { "Ohio", "OH" }
, { "Oklahoma", "OK" }
, { "Oregon", "OR" }
, { "Pennsylvania", "PA" }
, { "Rhode Island", "RI" }
, { "South Carolina", "SC" }
, { "South Dakota", "SD" }
, { "Tennessee", "TN" }
, { "Texas", "TX" }
, { "Utah", "UT" }
, { "Vermont", "VT" }
, { "Virginia", "VA" }
, { "Washington", "WA" }
, { "West Virginia", "WV" }
, { "Wisconsin", "WI" }
, { "Wyoming", "WY" }
};
}
Data that lives in small lists and infrequently changes, like the Grades
, StatesDictionary
class, exists in all types of applications.
Creating the Model
Now we need a model that collects the student’s name, the grade, and the state he is from as shown in Figure 1. The StudentModels
class definition captures these features in simple data structure. Let us now create a StudentModels
class as shown below. Create a file StudentModels.cs under the Models folder and add the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace TagStatus.Models
{
public class StudentModels
{
[Display(Name = "Student Name here")]
[Required()]
public string StudentName { get; set; }
public string Grade { get; set; }
public string State { get; set; }
}
}
Creating the ViewModel
Because of the data coming from different sources that needs to be displayed in the view, coding and maintenance will be easier if you use a ViewModel. ViewModels are a combination of one or more types that together shape data that goes to the view for consumption and rendering. A ViewModel is just a class. To get started, create a new folder named ViewModels and add a new code file StudentViewModel.cs to it. To create the StudentViewModel
ViewModel, add the StudentModels
, Grades
, and StatesDictionary
types as properties to form one StudentViewModel
class. In the source code below, the StudentViewModel
class contains the newly defined properties.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using TagStatus.Models;
namespace TagStatus.ViewModels
{
public class StudentViewModel
{
public StudentModels Student { get; set; }
public Grades Grades { get; set; }
public StatesDictionary States { get; set; }
public StudentViewModel(StudentModels student)
{
Student = student;
Grades = new Grades();
States = new StatesDictionary();
}
public StudentViewModel()
{
Student = new StudentModels(); ;
Grades = new Grades();
States = new StatesDictionary();
}
}
}
Creating the Controller
After creating the ViewModel, the next steps are to instantiate it in a controller and return it to the view. I am doing R & D on this new MVC4 and so you will see some messy code.
The following code creates a Controller Passing the ViewModel to the View and the code of interest for us is public ActionResult StudentInfo()
and this gets executed while the application is firing up.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TagStatus.Models;
using TagStatus.ViewModels;
namespace TagStatus.Controllers
{
public class StudentController : Controller
{
public ActionResult Index()
{
StudentModels stdm = new StudentModels();
StudentViewModel stdvm = new StudentViewModel(stdm);
return View(stdvm);
}
[AllowAnonymous]
[HttpPost]
public ActionResult StudentInfo(StudentModels model)
{
if (ModelState.IsValid)
{
model.StudentName = model.State;
}
return View(model);
}
[AllowAnonymous]
public ActionResult StudentInfo()
{
StudentViewModel model = new StudentViewModel();
return View(model);
}
}
}
Creating HTML5 Mobile Forms in ASP.NET MVC 4 Views:
In Visual Studio 2010, Right click on project and Add New Item command to create StudentInfo.cshtml, the view that will host your HTML5 form. Inside the view, various ASP.NET MVC 4 Html Helpers present components of the StudentViewModel
by rendering HTML elements that best fit the data types they map to in the ViewModel. For example, Grade renders as an HTML drop-down list so that the user can easily select an item rather than manually enter it.
The code for StudentInfo.cshtml is as follows.
@model TagStatus.ViewModels.StudentViewModel
@{
ViewBag.Title = "Student Info";
}
<h2> New Student </h2>
@using (Html.BeginForm("Results","Home"))
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Enter Student Information!</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Student.StudentName)
</div>
<div class="editor-field">
@Html.TextBoxFor(Model => Model.Student.StudentName)
@Html.ValidationMessageFor(Model => Model.Student.StudentName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Student.Grade)
</div>
<div class="editor-field">
@Html.DropDownListFor(Model => Model.Student.Grade, Grades.GradeSelectList)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Student.State)
</div>
<div class="editor-field">
@Html.DropDownListFor(Model => Model.Student.State, StatesDictionary.StateSelectList)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Download the source code from here and make the following change in the Global.asax file.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
//defaults: new { controller = "Tag", action = "Lookup", id = UrlParameter.Optional }
defaults: new { controller = "Student", action = "StudentInfo", id = UrlParameter.Optional }
//defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you don’t go after what you want, you’ll never have it. If you don’t ask, the answer is always no. If you don’t step forward, you’re always in the same place. – Nora Roberts