All JsAction features

In this section we will see several examples that will help you understand all JsAction features.

Q: Can JsAction rename Javascript method name?
A: Yes, it can. You have just to specify method name in named parameters.

        [JsAction(MethodName="GimmeTheString")]
        public string MyTestMethod(int a, int b)
        {
            return (a+b).ToString();
        }

$(document).ready(function () {
    var ret = JsActions.Home.GimmeTheString(1, 2, {
        success: function (data) {
            alert(data);
        }
    });
});

Q: What if i have a NoAction and JsAction attribute on the same controller method?
A: Methods marked with NoAction will be automatically skipped. No javascript will be generated.

Q: What if am i using ActionName attribute?
A: No problem, JsAction will adjust method call. Javascript function name will be the same of ActionName named parameter.

Q: Can JsAction handle HttpPost, HttpGet and AcceptVerbs attributes?
A: Yes, it can. JsAction will take care of HttpPost and HttpGet attribute and will modify ajax call according to your choice; the same with AcceptVerbs.

Q: Will JsAction take care of all other verbs?
A: No. I choosed it according to jQuery documentation:
Other HTTP request methods, such as PUT and DELETE, can also be used here, but they are not supported by all browsers.

Q: What if i do not specify any verb attribute or do i choose both
A: This is a good question.
When JsAction matches 2 verbs (specifying 2 attributes, or not specifying them, or elsewhere when can't find ONE matching attribute), if debug is attached, it will warn (Debug.WriteLine) you and use GET request.
This behavior has been changed in 0.3 release. See below to understand what will happen now!
On production environment, it will throw exception since he does not know which one to choose.

Q: So, am i forced to use JsAction only in single verbs actions?
A: No. If you have two o more verbs, you just tell your preferce to JsAction, using named parameter

        [HttpGet]
        [HttpPost]
        [JsAction(Verb = HttpSingleVerb.HttpGet)]
        public string MyTestMethod(int a, int b)
        {
            return (a + b).ToString();
        }
JsAction will generate javascript to call method using a GET request.

Q: Why have you defined a new enum for HttpVerbs? Was HttpVerbs in System.Web.Mvc suitable?
A: No, becouse it has got the FlagAttribute. HttpVerb must be unique, actually.

Q: Can't JsAction now generate multiple methods for multiple verbs?
A: Not actually, because i'm still studing how to manage it, but will be able to in next release. However, i think it should be not a particular limit for now.

New questions after 0.2 release

Q: What about caching Ajax requests?

A: Request caching can be controlled using cache value of jQuery options object. However, since i think cache control is important (expecially in time dependant server methods),and it's very annoing set it every time using option override i added a cache boolean value that will be written directly into jQuery options:

        [JsAction(CacheRequest = true)]
        public ActionResult MyTestMethod(int a, int b)
        {
            return Json(new { Name = "Test", Number = 3 });
        }
Its default value is true, following jQuery default options object.

Q: I do not want to use callbacks function or Promise interface. I want JsMethod return data, directly. Can JsAction?
A: Yes, it can. Anyway it's not a JsAction feature, you just have to play with jQuery options. At first, you have to set the async option = false. This will prevent method return until data will be ready. Once that, you can read the responseText (for plain data and json objects) or responseXml propery to get your value.

var ret = JsActions.Home.MyTestMethod(1, 2, {async:false}).responseText;

Since i use a lot this feature for DataSources (used, for example by http://www.kendoui.com or http://www.sencha.com/products/extjs/), i placed async boolean value as a named parameter. In this way, you can mark data returning methods and use them in a easier way:

Mark async in C# code:
        [JsAction(CacheRequest = true, Async = false)]
        public ActionResult MyTestMethod(int a, int b)
        {
            return Json(new { Name = "Test", Number = 3 });
        }

Avoid override jQuery options:
var ret = JsActions.Home.MyTestMethod(1, 2).responseText;

My aim, as you can see, is to never let you touch options object throught Javascript code, if not for special custom needs.

Q: What is the Groups named parameter purpose?
A: Suppose you have got a MVC project with several areas, a lot of controller and methods, and have some of them marked with JsAction attribute.
JsAction route handler will take ALL marked methods and generate Javascript code for you. This may be a resource wasting if
  • Methods are a lot
  • In a particular view or area, you are using only some methods

Q: So are you saying me i'll be able to import a subset of all JsAction methods?
A: Exaclty. Here is a quick how to:

At first, mark your methods with the same group name.
        [JsAction(Groups="Area1Methods")]
        public ActionResult MyTestMethod(int a, int b)
        {
            return Json(new { Name = "Test", Number = 3 });
        }

Use the Html helper and choose group to import:

    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="../../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
    @Html.JsScript("Area1Methods")
    <script src="../../Scripts/script.js" type="text/javascript"></script>

In this way, only methods of Area1Methods will be imported.

To minimize HTTP requests, you can import multiple groups in one time

    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="../../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
    @Html.JsScript("Area1Methods","Area2Methods")
    <script src="../../Scripts/script.js" type="text/javascript"></script>

Calling Html.JsScript() with no parameters will import ALL methods marked.

A method can be part of one or more groups, specifying them using comma:
        [JsAction(Groups="Area1Methods,Area2Methods")]
        public ActionResult MyTestMethod(int a, int b)
        {
            return Json(new { Name = "Test", Number = 3 });
        }

This will let you create adhoc groups that should suite with your needs.

New questions after 0.3 release

Q:What's new:
This is manly a manteinance release.
  • At first, javascript generated code uses no more the $ snippet to access jQuery object, since the $ (as jQuery doc say) can be relinquished in favor of prototype or other javascript libraries. All jQuery calls are now made using jQuery object, and no conflicts should happen.
  • I noticed that, when having a Verb issue, a simplie OutputDebugString was not enough to see the problem. When using library, most time of did not noticed of mistake for days and days, and this was no good. To force you see the problem (and, eventually, solve it), when a debug is attached and a Verb mistake is detected, HTML code will be injected in page showing you the error.
Here is a sample image:
Img.png

Yes, i definitely should improve its look, but it does its job and it's enough for now!
  • Code generation speed has been improved a lot. From 3117 ms is now only 117 ms. This was becouse Attribute reserach was performed in all AppDomain assemblies, involving also standards one that surely can't have JsAction attribute (such System.Web.Mvc, System.Data.Entity and so on). JsAction handler will now look up only current assembly, by default.

Q: And what if i have external controllers marked with JsAction attribute? Will they be ignored?
A: The short answer is not for now. I'm studing a method to do it and i will publish it when it will be finished.
The long answer is yes using a workaround but it's not the best solution:

This code:
routes.Add("JsActionRoute", JsAction.JsActionRouteHandlerInstance.JsActionRoute);

Must be replaced with a custom Route entry creation:
routes.Add("JsActionRoute",new JsAction.JsActionHandler(MyAsm1,MyAsm2,MyAsm3);
NOTE: Current assembly MUST be specified. Just make a call to Assembly.GetCallingAssembly or the best one that fits your needs.

New questions after 0.3.2 release

Q: What's new?
This is a intermediate release (before 0.4) added to support complex types binding.
You can read what was going wrong reading Complex types work item.
Consider a "simple" complex type:
    public class ComplexType
    {
        public int t1 { get; set; }
        public string t2 { get; set; }
    }

Asp.net MVC has built-in capabilities of transforming sent data to strong type objects. But data we're sending has to be prepared in the right way so default data binder can it and populate controller action parameters objects' properties.

The problem is that providing JSON object to jQuery.ajax() function call doesn't work. At all. Data doesn't get data bound on the server so controller action parameters have their default values that are probably invalid anyway.

The problem is that JSON object got converted by jQuery to request query string and second level property values got mangled into a form that Asp.net MVC default model binder doesn't understand

So i addead a jQuery plug-in that is able to covert any JSON object with any sub object depth and also support arbitrary arrays that will be correctly data bound to IList on the server side.

So now you can send complex json objects directly:

JsActions.ComplexAction({t1:1, t2:'String string string'},3,4,'testString').then(function(q){alert(q);});
NOTE: You have to send REAL objects, not strings created using stringify function.

Q: And what about Areas routing?
This is a very difficult point.
JsAction has no ability to understand that a controller is in a particular area. Why?
For the way areas are currently implemented, the MVC framework doesn't really know about them. They are just special routes. This is one of the fields in which the team will improve in the future version and make areas first class citizens.
Q: And meanwhile?
Meanwhile nothing. currently there isn't an easy and reliable way to do this In ASP.NET MVC 4.0 though there will be, I promise (MVC team member response).

Currently i might need to use reflection and look at all namespaces containing Areas.something and count them. Not very reliable. I refuse to code (and also use) something using a way like this to obtain a feature.
Instead, if needed, create a big large controller with all Actions that may be called using Ajax in the root area, and make all your ajax call there.

Last edited Feb 26, 2012 at 3:05 PM by XVincentX, version 24

Comments

XVincentX Jun 13, 2012 at 5:38 PM 
Please open an issue, i'm not mailed about your comments!

tommck May 2, 2012 at 6:06 PM 
Again, using the wrong script method? @Html.JsScript() is deprecated and the [Obsolete] tag directs you to a method that doesn't exist..

Luis_Fernando Jan 6, 2012 at 11:36 PM 
Thanks for the complex types support, you are great!