Click here to Skip to main content
Click here to Skip to main content

Some points to consider while working on ASP.NET MVC and jQuery

, 27 Apr 2012
Rate this:
Please Sign up or sign in to vote.
Some points to consider while working on ASP.NET MVC and jQuery

Introduction

ASP.NET MVC as we know provides:

  1. A pattern based approach to build websites.
  2. Clear separation of concern
  3. Follows the latest web standards

These capabilities along with jQuery, which is a light weight JavaScript library with some very useful out of the box functions, greatly improves as well simplifies to some extent the work of web application developers. For the latest jQuery version and the accordingly the "js" file to be included, refer to this link.

In this article, based on my past coding experience, I will try to identify certain points which if taken into consideration may help a new developer leverage the above mentioned capabilities better. This article may also be helpful in fixing quite a few types of errors, otherwise looking for the corresponding resolution would be time consuming.

Assumption

The reader is aware of basic terminologies of ASP.NET MVC (e.g., model, view, controller, etc.) and jQuery (e.g., function, selector, etc.).

1. How data is mapped and passed between the client and server side

Many a times it comes as a surprise to a beginner, on how data is so correctly mapped and passed from the client to the server and vice-versa. In our earlier web application programming practice many of us must have used "HTML hidden variables" to pass data from the client to the server side and vice versa. The ASP.NET MVC framework also takes this concept into consideration.

In ASP.NET MVC, whenever we want to send some data from the client side to the server side using the Model object instance, we use the following kind of HTML control (e.g., check box) syntaxes in the concerned view:

<% : Html.CheckBoxFor(m => m.SomeBooleanModelProperty,
new { any attributes for the check in the key-value format, e.g. onclick="fnCheckSelection();"
})%> 

Where m depicts the model to which the view is bound to. And note CheckBoxFor, not just CheckBox.

Once this control is rendered in the client’s browser, if we look into the HTML source (i.e., through View-Source in IE), we will notice that a hidden variable is also added by the same name as the check box. This "name" is same as the property in the model of the view:

<input name="SomeBooleanModelProperty " onclick="fnCheckSelection();" type="checkbox" value="true" />
<input name=" SomeBooleanModelProperty " type="hidden" value="false" />

Thus the check box selection (i.e., Boolean depicting checked or unchecked) gets propagated to the server side on post back. Similarly we may pass text using Html.TextBoxFor.

So in case you need to pass or assign back some parameter of the model which is not directly linked to some control (as above) but it needs to be made available in the post back, e.g., to some controller action like:

[HttpPost] 
public ActionResult Save(SomeModelObject model) 
{ 
    //some code here 
}

We can make use of hidden type control as:

<input name="<%= "ProjectName" %>" type="hidden" value="<%= Model.ProjectName %>" /> 

Where ProjectName is some property in the model. If we don’t do this then in the action argument model object, the value for this property will be missing, i.e., if the type is string, the value will be null after postback.

2. An optional parameter must be a nullable type

Suppose we have an action in the controller like:

public ActionResult Index(string id) 
{
    // do something… 
}

This action can be targeted by either of the following GET calls without any issues:

Complete REST based URLs: http://localhost:portNumber/controllerName/index or http://localhost:portNumber/controllerName/index/someID.

In the first option, the value passed to the "id" argument will be null. So we need to always make sure in such a scenario where the argument is optional, the type of the argument should be nullable otherwise we will get an error messages like:

An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.

In the above example, by default the URL provided will hold good only if the variable name in the action signature is "id". If the variable is something else like identity, e.g.:

public ActionResult Index(string identity) 
{ 
   // do something… 
}

Then the GET URL has to like be like: http://localhost:portNumber/controllerName/index?identity=someValue.

Otherwise even after providing the right value, the value of the parameter will be null when the action is called. In the next point, we will see how to have a complete REST based GET URL even for parameters with names other than "id".

3. Complete REST based GET URL with parameter other than "id"

By default when an ASP.NET MVC project is created in Visual Studio, in the newly created web-application, the following method is added in Global.asax.cs:

public static void RegisterRoutes(RouteCollection routes) 
{ 
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
    routes.MapRoute( 
    "Default", // Route name 
    "{controller}/{action}/{id}", // URL with parameters 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 
    ); 
}

Change id to a more plausible variable name, e.g., projectCode, and then we can have an action in the controller by signature:

public ActionResult Index(string projectCode) 
{ 
// do something… 
}

4. Passing more than one parameter to the GET action as a complete REST based short URL

In the above mentioned method in Global.asax.cs, change the list of parameters as:

public static void RegisterRoutes(RouteCollection routes) 
{ 
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
    routes.MapRoute( 
    "Default", // Route name 
    "{controller}/{action}/{ projectCode }/{nextParam}", // URL with parameters 
    new { controller = "Home", action = "Index", projectCode = UrlParameter.Optional, 
          nextParam = UrlParameter.Optional } // Parameter defaults 
    ); 
}

Where projectCode = UrlParameter.Optional infers that projectCode is optional, otherwise we may provide a default value in case it is not optional as required. And hence we may have an action signature like:

public ActionResult Index(string projectCode, string nextParam) 
{ 
    //do something… 
}

This action could be then accessed as:

http://localhost:portNumber/controllerName/index/someProjectCode/someOtherParam.

In this way, the URL can be short otherwise we would have required to follow the QueryString based approach with the name of the parameters also mentioned in the URL.

Similarly we have any number of levels in the URL but we need to make sure that the length will never exceed the allowed characters for the browser in concern. For example, for IE8 and IE9, I believe it is 2083 characters. Check the same for other browsers. A good option would be to restrict within 256 characters to support all browsers.

5. Parameter-less constructor for Model object

During the call to any action in a controller of type:

[HttpPost] 
public ActionResult Save(SomeModelObject model) 
{ 
  //some code here 
}

The ASP.NET MVC framework tries to instantiate an object of type of the model and while doing so, it expects a parameter-less constructor in the model definition. Many a times we must get error messages like:

No parameterless constructor...

To resolve such an error, we need to make sure that the model definition has at least a parameterless constructor.

6. jQuery function not called

Many a times while leveraging jQuery, to register an event for certain kinds of controls, e.g., click event on the "input" HTML element, we write functions as:

$("input#btnSave").click(function (event) { 
  if ($(this).attr('disabled')) { 
    return false; 
  } 
  // do something… 
});

Where btnSave is the ID of an input type HTML element.

But during runtime, on click of the "input" HTML element with id = btnSave, the click event is not invoked. This is a very frequently encountered issue where the HTML element fails to locate the right event. To resolve this as a best practice, for all such event registrations, we should put the above code in the document-ready event as:

$(document).ready(function () { 
$("input#btnSave").click(function (event) { 
if ($(this).attr('disabled')) { 
return false; 
} 

// do something… 
});

This helps avoid any jQuery code from running before the document has finished loading. And thus registers the event for specific controls in the document only after all the controls are loaded.

7. JSON is not defined

Even while using the latest internet browser which is expected to support JSON, we may get error message like:

JSON undefined.

for code like:

JSON.stringify(someObject)

Such errors are frequent in older versions of internet browsers and even if you receive such errors for any (probably existing) application in new versions of browsers, the likely reason is, somewhere in the view or master page, the code might be restricting the execution of the browser (though new) like an old version. For example, in IE 8 browser, I can configure in the application to imitate and restrict to IE 7 (i.e., use the capability and feature of IE 7 only) by providing:

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

Either in the respective ASPX page or the master page.

To resolve the "JSON undefined.." issue, make sure to remove such tags. But in case there is a genuine reason to restrict to the old version of browser but yet need to use some new JSON capabilities like the "stringify" mentioned above, then explicitly include the corresponding function in the view, e.g.:

<script type="text/javascript"> 
var JSON = JSON || {}; 
// implement JSON.stringify serialization 
JSON.stringify = JSON.stringify || function (obj) { 
var t = typeof (obj); 
if (t != "object" || obj === null) { 
// simple data type 
if (t == "string") 
obj = '"' + obj + '"'; 
return String(obj); 
} else { 
// recurse array or object 
var n, v, json = [], arr = (obj && obj.constructor == Array); 
for (n in obj) { 
v = obj[n]; 
t = typeof (v); 
if (t == "string") 
v = '"' + v + '"'; 
else if (t == "object" && v !== null) 
v = JSON.stringify(v); 
json.push((arr ? "" : '"' + n + '":') + String(v)); 
} 
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}"); 
} 
}; 
</script>

This way the code will run in both new as well as old browser versions. For the definitions for other JSON functions like the one depicted above, refer to this link.

8. Asynchronous GET and POST controller action call

To provide a better user experience, many a times we are required to fetch as well as send data from/to the server asynchronously. For such requirements, AJAX based jQuery methods are very helpful.

E.g., for GET call:

function fnGetData() { 
$.ajax( 
{ 
    type: "GET", 
    url: "/ControllerName/ActionName", 
    data: "param1=value1" + "&param2=value2" , 
    success: function (data, status) { 
    // do something with "data" 
    }, 
    error: function (req, status, error) { 
    alert("Error occurred while processing your request. Please contact the Administrator."); 
    } 
    }); 
}

Where data is the returned object from the controller-action which could be like:

public ActionResult ActionName (string param1, string param2) 
{ 
  //do something… 
}

E.g., for POST call:

function fnPostData() { 

//structure the parameter data to be posted 
$.ajax( 
{ 
    type: "POST", 
    url: "/ControllerName/ActionName", 
    data: "parameter1=parameterData1" + "&parameter2=parameterData2", 
    success: function (data, status) { 
    //do something with the "data" 
    }, 
    error: function (req, status, error) { 
    alert("Error occurred while processing your request. Please contact the Administrator."); 
    } 
    }); 
}

Where data is the returned object from the controller-action which could be like:

[HttpPost] 
public ActionResult ActionName (string parameter1, string parameter2) 
{ 
  //do something… 
}

The best part I believe here is, the data sent to the server is not part of the URL string but part of the message body.

History

First improved version on- 04/27/2012

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution-Share Alike 3.0 Unported License

Share

About the Author

Rahul Bandopadhyaya
Architect Infosys Limited
India India
I am currently working as Technical Architect in Infosys Limited and My motto is "share the good knowledge so that my fellow colleague or those from the same profession may not face the same issue as I have had… enjoy coding and sharing knowledge"
 
"Disclaimer: Any views or opinions presented in this article are solely those of the author and do not represent those of Infosys Limited. Infosys Limited does not accept any liability in respect of any views or content present herein."

Comments and Discussions

 
QuestionThanks for sharing PinmemberPatrick Kalkman27-Apr-12 8:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 27 Apr 2012
Article Copyright 2012 by Rahul Bandopadhyaya
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid