Introduction
Postback the page data changes as model object to controller action method in MVC3 environment. The same model class can be used for the both GET
/POST
operations.
The sample uses Visual Studio 2010 with MVC3 Razor, jquery-1.4.2.
I feel MVC3 in VS2010 is just not an architectural change but gives more freedom to the developer in - view, controller or model coding, besides its advantages.
Background
The basic necessity for any MVC3 view(screen) is to populate the “model” then on postback send the changes to server as either model(data class) or form fields.
This is not painful if the developer starts from scratch because HtmlHelper
or scaffolding is always available to bind model and create HTML elements.
Nevertheless, this technique can also be used for the new views created from scratch.
But it is hard and confusing when you have an HTML file and would like to convert to MVC3 view that uses model for both get
/post
operations.
In my project, one group converted the existing classic ASP to pure HTML (actually the .cshtml) with all the required styles. We had to use them in MVC3 environment to bind the model we already created while the other group was converting ASP to HTML.
It is a 3-step technique! - create a JavaScript function, bind & set class name in cshtml and create model action methods (GET
/POST
).
Advantages:
- Sends all or selected fields to server. The best thing is it binds to model at any level - I can set a property of a class object inside my model class. Example:
Address.Street
.
- Uses same model class for
GET
/POST
.
- You may not need – form control, submit button, name attribute to element. A button element is enough to trigger the JavaScript.
- No page flickering (refresh) because of AJAX.
Using the Code
Step - 1
Add a class attribute to the cshtml element that you want to postback as model
property. The Model
property is a public get
/set
method in your model
class. The class value is "prefixModelProperty
". Example: prefix = “ModPrfx-“ ; ModelProperty=”Address.Street”
.
<div id="divParent">
<input type="text" class="textcls ModPrfx-Address.Street"
value="@Model.Address.Street" id="txtSt"/>
<input type="checkbox" class="ModPrfx-IsManager" id="chk"/>Is Manager
<input type="text" class="textcls ModPrfx-Name" value="@Model.Name" id="txtNm"/>
@Html.DropDownList("ddlState", new SelectList(Model.States, "State", "State",
Model.Address.State), "", new { @class = "ModPrfx-Address.State" })
<input type="button" value="Save" id="btnSave"/>
</div>
<div id="divGen">
Male:<input type="radio" name="gender" class="ModPrfx-Gender" value="1"/>
Female:<input type="radio" name="gender" class="ModPrfx-Gender" value="2"/>
</div>
Step - 2
Register a JavaScript function to a save button. This calls “Employee
” method in controller. The SerializeForAjax()
takes three parameters. The first is parent control, next is prefix and the last is already populated JSON object (optional).
$(document).ready(function () {
$("#btnSave").click(function (e) {
e.preventDefault();
$.ajax({
url: <a href="mailto:'@Url.Action(%22Employee%22)'">'
@Url.Action("Employee")'</a>,
type: 'post',
dataType: 'json',
data: SerializeForAjax('divParent','ModPrfx-',
SerializeForAjax('divGen','ModPrfx-')),
success: function (data) {
if (data && data.error) {
alert(data.error);
}
}});});});
Step - 3
Define Employee GET
/POST
methods inside controller class.
public ActionResult Employee()
{
Employee model = new Employee();
model.Load(Session);
return View(model);
}
[HttpPost]
public ActionResult Employee(Employee model)
{
if(!Request.IsAjaxRequest())
return Json(""); object results = model.Save(Session,User.Identity.Name);
return Json(results);
}
public class Employee
{
public EmpAddress Address { get; set; }
public bool IsManager { get; set; }
public string Name { get; set; }
public int Gender { get; set; }
public void Load(HttpSessionStateBase session) {..}
public object Save(HttpSessionStateBase session, string user) {..}
…….
}
public class EmpAddress
{
public string Street { get; set; }
public string State { get; set; }
}
The SerializeForAjax()
is the main JavaScript method that serializes the HTML elements' data into a JSON object to be sent to the server. SerializeForAjax
is good for all the input elements. I tested for text
, textarea
, checkbox
, radio
, and select
elements.
function SerializeForAjax(prntCtrlId, modelFieldPrefix,jsonObj) {
var mdlFldWord = arguments.length == 1 ? "model-field-" : modelFieldPrefix;
var jsonData = !jsonObj ? {} : jsonObj;
var testData = "", modelFld = "";
$("#" + prntCtrlId + " [class*=" + mdlFldWord + "]").each(function (index, value) {
var classes = this.className.split(" "); for (var i = 0; i < classes.length; i++) {
if (classes[i].indexOf(mdlFldWord) == 0) {
modelFld = classes[i].substring(mdlFldWord.length);
break;
}
}
var dataVal, dataOk = true;
if ($(this).is(':text')) {
dataVal = !$(this).data(this.id) ? this.value : $(this).data(this.id);
}
else if ($(this).is(':checkbox')) {
dataVal = this.checked;
}
else if ($(this).is(':radio')) {
dataVal = this.checked ? $(this).val() : "";
dataOk = this.checked;
}
else if ($(this).is('select')) {
var tdata = $(this).val();
if (tdata && typeof tdata == 'string') {
dataVal = tdata;
}
else { for (var i = 0; tdata && i < tdata.length; i++) {
dataVal = (dataVal) ? (dataVal + "," + tdata[i]) : tdata[i];
}
}
} else {
dataVal = $(this).val();
}
if (dataOk) {
if (modelFld.indexOf("-") > -1) {
modelFld = modelFld.replace("-", ".");
}
jsonData[modelFld] = dataVal;
}
});
return jsonData;
}
Points of Interest
You can extend SerializeForAjax()
to implement a validation callback for the user (developer).
History
- 8th December, 2011: Initial post