Setting ajax url for jQuery in JS file using ASP.NET MVC
Solution 1
This way fully uses MVC Routing so you can fully take advantage of the MVC framework. Inspired by stusmith's answer.
Here I have an action in ApplicationController
for dynamic javascript for this URL :
/application/js
I'm including static files here because I want just one master javascript file to download. You can choose to just return the dynamic stuff if you want:
/// <summary>
/// Renders out javascript
/// </summary>
/// <returns></returns>
[OutputCache(CacheProfile = "Script")]
[ActionName("js")]
public ContentResult RenderJavascript()
{
StringBuilder js = new StringBuilder();
// load all my static javascript files
js.AppendLine(IO.File.ReadAllText(Request.MapPath("~/Scripts/rr/cart.js")));
js.AppendLine(";");
// dynamic javascript for lookup tables
js.AppendLine(GetLookupTables());
js.AppendLine(";");
return new ContentResult()
{
Content = js.ToString(),
ContentType = "application/x-javascript"
};
}
This is the helper function that creates our lookup table. Just add in a line for each RouteUrl you want to use.
[NonAction]
private string GetLookupTables()
{
StringBuilder js = new StringBuilder();
// list of keys that correspond to route URLS
var urls = new[] {
new { key = "updateCart", url = Url.RouteUrl("cart-route", new { action = "updatecart" }) },
new { key = "removeItem", url = Url.RouteUrl("cart-route", new { action = "removeitem" }) }
};
// lookup table function
js.AppendLine("// URL Lookuptable");
js.AppendLine("$.url=function(url) {");
js.AppendLine("var lookupTable = " + new JavaScriptSerializer().Serialize(urls.ToDictionary(x=>x.key, x=>x.url)) + ";");
js.AppendLine("return lookupTable[url];");
js.AppendLine("}");
return js.ToString();
}
This generates the following dynamic javascript, which is basically just a lookup table from an arbitrary key to the URL I need for my action method :
// URL Lookuptable
$.url=function(url) {
var lookupTable = {"updateCart":"/rrmvc/store/cart/updatecart","removeItem":"/rrmvc/store/cart/removeitem"};
return lookupTable[url];
}
In cart.js I can have a function like this. Note that the url parameter is taken from the lookup table :
var RRStore = {};
RRStore.updateCart = function(sku, qty) {
$.ajax({
type: "POST",
url: $.url("updateCart"),
data: "sku=" + sku + "&qty=" + qty,
dataType: "json"
// beforeSend: function (){},
// success: function (){},
// error: function (){},
// complete: function (){},
});
return false;
};
I can call it from anywhere with just :
RRStore.updateCart(1001, 5);
This seemed to be the only way I could come up with that would allow me to use routing in a clean way. Dynamically creating URLS in javascript is icky and hard to test. Testing types can add in a layer somewhere in here to easily facilitate testing.
Solution 2
The way I do it is generate the URL server-side and store in the generated HTML using an HTML5 data attribute, eg: (Razor syntax)
<li class='customClass' data-url='@Url.Action("DisplayItems", "Home", new { id = Model.Id })'>...</li>
Then you can use the jQuery attr() function to pick up the url, eg:
$(".customClass").click(function () {
$.ajax({
url: $(this).attr("data-url"),
success: function (data) {
// do stuff
}
});
});
If you're generating HTML client-side in response to AJAX calls, you can include the relevant URLs in your JSON payload and populate the data- attribute the same way.
Solution 3
Wrap the AJAX call in a function that takes the URL (and any other data) as a parameter(s) and returns the response. Then in your view, call the function instead of calling the AJAX call directly.
function doAjax( url, data, elem, callback )
{
return $.ajax({
url: url,
data: { ajax: data },
cache: false,
success: function(response) { callback(response, elem, xhr); }
});
}
...
<input type='button' value='Go get it' onclick='doAjax( <%= Url.Action ...
I'm not sure that this is any better than having the Ajax call on the page instead of in a JS file, unless you use the exact same pattern frequently.
Solution 4
Use the module pattern.
// separate js file
var PAGE_MODULE = (function () {
var url = {},
init = function(url) { ... },
load = function() {
$.ajax({
url: url,
...
});
}
return { init: init };
})();
// calling init goes on the page itself
PAGE_MODULE.init(" %: Url.Action(...) %>");
In general the inline onclick handler is not good javascript as you are using a global function.
onclick='doAjax(
I recommend reading http://jqfundamentals.com/book/index.html#N20D82 to get a better handle on the module pattern.
Related videos on Youtube
Schotime
Senior Developer from Melbourne, Australia. Interested in all things Dev with particular interest in C#, MVC, PetaPoco, Spark and FluentValidation. Outside of work I enjoy Golf, Basketball and Ice Hockey!
Updated on July 09, 2022Comments
-
Schotime almost 2 years
When doing a Ajax call to an MVC action currently I have my javascript inside the View, not inside its own JS file.
It is then very easy to do this:
var xhr = $.ajax({ url: '<%= Url.Action("DisplayItem","Home") %>/' + el1.siblings("input:hidden").val(), data: { ajax: "Y" }, cache: false, success: function(response) { displayMore(response, el1, xhr) } });
...then including the URL in the ajax call using
Url.Action()
inside the JS is pretty easy. How could i move this do its own JS file when without hard coding the URL? -
Schotime over 15 yearsisn't this the inline JS we are trying to remove by hooking up the events on document.ready???
-
BobbyShaftoe over 15 yearsYour solution is technically right, but I agree with you that this is not really better.
-
aruno almost 15 yearsit depends what you're trying to abstract away into the library function. in this case you're not abstracting any business logic away, just hiding the fact that you're using jquery
-
mare over 13 yearsthis is so nice and very clean indeed!
-
Dan Atkinson over 13 years-1 for using PageLoad in ASP.NET MVC. Although it could be changed to use something else...
-
stusmith over 13 yearsI must confess I didn't read the question properly... I thought it was an ASP.NET one. Apologies.
-
aruno over 13 yearsjust be aware that you're revealing all your paths to your action methods here. make sure you have adequate security for action methods that need security - and that you may potentially get more spidering requests to those methods (unverified)
-
sotto about 13 yearsyou might counter that with a robots.txt file?
-
Valamas about 12 yearsThis should be the #1 answer. For those that are adding html attributes serverside....
data_url
translates in the html source todata-url
. -
Sam over 10 yearsA minor update: later versions of jQuery support accessing data attributes directly using the
data()
function (e.g.$(this).data('url')
). -
tonyapolis about 9 yearsSlick! Helped me out almost 4 years later
-
crush almost 9 years@Valamas-AUS This is a good answer, and will suffice for many situations, but I'm not sure it's the BEST answer. I think the idea of generating a lookup table is still a better answer, though I don't agree with the implementation presented above. My issue with the
data-url
approach is that it assumes there is a DOM element attached to every needed route. That's often not the case in my experience. -
crush almost 9 yearsSecurity by omission, or security by obscurity, is never a good policy anyways. Even if you weren't sending all available routes to the browser, one should never assume that they couldn't be "discovered". I'm sure you will agree, but just wanted to add that point.
-
Sam almost 9 years@crush - there’s no such thing as best :) ‘Modern’ js would probably do things differently. No doubt there’s a framework for that.
-
crush almost 9 years@Sam It's not the "best" because it doesn't cover all usage cases, like the one I presented - Unless you are condoning adding hidden elements to the DOM just to insert route data into the page. Has nothing to do with "modern" js. I gave you a compliment. I'm not sure why you felt the need to come in and try to defend yourself here, but I didn't mean any disrespect. I was simply pointing out a deficiency in the answer so future users would consider it in their individual usage case.
-
Sam almost 9 yearsSorry, I’m not trying to be defensive, it’s just a bugbear of mine when people equate ‘best’ with ‘horribly over-engineered’, or think that solutions need to be able to solve problems they don’t (yet) have. The simplest solution that solves your problem is usually the best. I appreciate your point that this won’t work well for scenarios (that I’ve clumsily described as ‘modern’) that are more complex than hanging event handlers off DOM elements as we did in the days of yore.