I\'ve got a fairly simple question for all the Razor experts out there. I\'m trying to make a jQuery $.ajax() call to a URL, using Url.Content() to translate the home-relat
Update: Dave's less than symbol was not causing the problem, he only added it in his question for illustrative purposes.
On MVC4 I was able to isolate the issue. This would not compile:
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '@Url.Content("~/Logging/GetMessages")',
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
}); //<-- test
});
</script>
But this would:
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '@Url.Content("~/Logging/GetMessages")',
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
}); //-- test
});
</script>
Seems like it was just the < in the comment that was throwing it.
Matthew's answer pretty much explains the behaviour (although, to be honest, I can't reproduce your problem - nor see why it wouldn't work - both your examples run just fine here). For a different approach, you could dedicate an action/view to generated javascript variables (urls, settings, localized texts, whatever), i.e.:
// Could/should be OutputCached depending on the scenario
public ActionResult Globals()
{
var model = new ClientGlobalsModel();
// ClientGlobalsModel has a single (could be more) Dictionary<string, string>
// (Urls) and a ToJSON() method which uses JavaScriptSerializer to serialize
// the object:
model.Urls.Add("GetMessages", Url.Content("~/Logging/GetMessages"));
// I mostly use this method for e.g. actions:
model.Urls.Add("UploadImage", Url.Action("Upload", "Image"));
Response.ContentType = "text/javascript";
return View(model);
}
Globals.cshtml:
@model ClientGlobalsModel
@{
Layout = null; // If you have a layout supplied in e.g. _ViewStart
}
var GLOBALS = @Model.ToJSON()
Yeah, this could have been a simple Content()
result rather than a view - but when you have more globals (e.g. settings + urls + texts), you may want easier control over the script output and maybe serialize each dictionary individually. May also want to namespace that "GLOBALS" variable in some shared application namespace to avoid polluting the global scope.
(e.g.) Index.cshtml:
<script src="@Url.Action("Globals", "Client")"></script>
<script src="@Url.Content("~/Scripts/main.js")"></script>
... which simply includes the output from /Client/Globals. And "main.js", into which we have now moved the rest of your script:
main.js (static):
$(document).ready(function () {
$.ajax({
url: GLOBALS.Urls.GetMessages,
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
});
});
You can, of course, use the same kind of approach to output a few user/context/view-specific settings directly into the view. For a few URL's or data, the data-* attribute approach may be better depending on your tastes. I'm not a fan of stuffing tons of what's basically settings into attributes on every HTML page, though.
Thanks for all of the help, folks.
It looks like it must have been a bug in the ASP.NET MVC 4 Preview. I just upgraded to the ASP.NET MVC 4 Beta which came out on February 15, and the problem is now completely gone.
This is likely down to the behaviour of the parser. When it encounters the @
symbol, the parser switches to code mode and will read the implicit expression Url.Content("~/Logging.GetMessages")
(well, actually it will read until the '
symbol, determine it is not a valid character in an expression and trackback to return until the end of the )
. It's after this stage that the parser is getting a little confused with your view because it's likely in code mode when it encounters the final }
character, and thinks it is the end of of a code span.
The reality is, you have to be quite careful when using javascript within a razor view with C#. The transitions to code are explicit, e.g. @
and after a {
, but the transitions to markup are a little harder to determine.
Razor aside, my recommendation would be to stick your javascript application code in an external file, and take advantage of data-* attributes to convey meta information to your application code, e.g:
<script id="messageScript" type="text/javascript"
src="/Scripts/messages.js"
data-messages="@Url.Content("~/Logging/GetMessages")"></script>
Which you can access as:
(function($) {
$(function() {
var $this = $("#messageScript");
$.ajax({
url: $this.attr("data-messages"),
type: "html",
success: function(result) {
$("tbody").html(result);
}
});
});
})(window.jQuery);
Seems to still be a problem with final MVC4 / Visual Studio 2010, but here is my fix:
@section jQueryDocumentReady
{
@{
<text>
// All the javascript and bracers you want here
</text>
}
}