Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Postback Model Object for Existing HTML using MVC3 and AJAX

0.00/5 (No votes)
8 Dec 2011 5  
Postback model object for existing HTML using MVC3 and AJAX

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:

  1. 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.
  2. Uses same model class for GET/POST.
  3. You may not need – form control, submit button, name attribute to element. A button element is enough to trigger the JavaScript.
  4. 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.

//[HttpGet]
public ActionResult Employee()
{
   Employee model = new Employee();
   model.Load(Session);
   return View(model);
}
[HttpPost]
public ActionResult Employee(Employee model)
{
   //we do not want non-ajax access to this method
   if(!Request.IsAjaxRequest())
       return Json(""); //or send to error page
   object results = model.Save(Session,User.Identity.Name);
   return Json(results);
}

//The sample model class looks like this:
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(" "); //what if multiple classes exist
        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')) {
            //if you store the unformatted data) to be saved/sent to server
            //can be stored into object. ex: i have text ctrl id="txtPhn" then
            //you have to save rawdata as $("#txtPhn").data("txtPhn","1234567890")
            //but you display as (123)-456-7890.
            dataVal = !$(this).data(this.id) ? this.value : $(this).data(this.id);
        }
        //checkbox 
        else if ($(this).is(':checkbox')) {
            dataVal = this.checked;
        }
        //radio button
        else if ($(this).is(':radio')) {
            dataVal = this.checked ? $(this).val() : "";
            //store data only when rb is checked 
            dataOk = this.checked;
        }
        //for ddl or combo box handling
        else if ($(this).is('select')) {
            var tdata = $(this).val();
            //if single select
            if (tdata && typeof tdata == 'string') {
                dataVal = tdata;
            }
            else { //multiselect
                for (var i = 0; tdata && i < tdata.length; i++) {
                    dataVal = (dataVal) ? (dataVal + "," + tdata[i]) : tdata[i];
                }
            }
        } //if its other than above objects. (ex:button,file,hidden,
     //image,password,reset,sumit)
        else {
            dataVal = $(this).val();
        }


      if (dataOk) {
     //if you want to separate object & method with - instead ' in class attribute value
            if (modelFld.indexOf("-") > -1) {
                modelFld = modelFld.replace("-", ".");
            }
            jsonData[modelFld] = dataVal;
            //testData = testData + modelFld + "=" + dataVal + "\n"
        }
    });
    //alert(testData);
    return jsonData;
}

Points of Interest

You can extend SerializeForAjax() to implement a validation callback for the user (developer).

History

  • 8th December, 2011: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here