Reputation: 15977
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?
Upvotes: 22
Views: 40844
Reputation: 4732
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.
Upvotes: 38
Reputation: 2049
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.
Upvotes: 3
Reputation: 145910
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.
Upvotes: 19
Reputation: 14093
Here's another way:
In your master page, include an area for inline scripts:
<head>
...
<asp:ContentPlaceHolder runat="server" ID="_inlineScripts" />
...
</head>
Then in the Page_Load, create a utility function:
protected void Page_Load( object sender, EventArgs e )
{
AddInlineScript( string.Format( "$.url=function(url){{return '{0}'+url;}}", GetBaseUri() ) );
...
}
private Uri GetBaseUri()
{
var requestUrl = Request.Url.AbsoluteUri;
var i = requestUrl.IndexOf( request.Path );
return new Uri( requestUrl.Substring( 0, i ) );
}
private void AddInlineScript( string content )
{
var script = new HtmlGenericControl( "script" );
script.Attributes.Add( "type", "text/javascript" );
script.InnerHtml = content;
_inlineScripts.Controls.Add( script );
}
Now you can use this function in your ajax:
$.ajax({
url: $.url('path/to/my-handler'),
...
});
Upvotes: -1
Reputation: 532435
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.
Upvotes: 6