MVC4 Razor confused about braces - asp.net-mvc

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-relative path into an root-relative path. In so doing, Razor is getting a bit confused about where the end of my #section is located. I'd prefer to be able to specify the URL inline, but when I do that, Razor thinks that the end of the $.ajax() parameter list is the end of my #section. I'm using #section because I want to use layouts to place my javascript at the bottom of each file. Why is Razor getting so confused? I've even tried using #(Url.Content(...)), but that doesn't work either.
Also, is this the best way to approach the problem? I'm using the ASP.NET MVC 4 Preview.
This works:
#section Scripts {
<script type="text/javascript">
var getMessagesUrl = '#Url.Content("~/Logging/GetMessages")';
$(document).ready(function () {
$.ajax({
url: getMessagesUrl,
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
});
});
</script>
}
This doesn't:
#section Scripts {
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '#Url.Content("~/Logging/GetMessages")',
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
}); //Razor thinks this is the curly-brace that ends the section!
});
</script>
}

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);

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.

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>
}
}

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.

Related

ASP.Net MVC how to invoke the action method every 10 seconds

Sorry if this is a silly question.
I have a simple MVC action method which retrieves data from database. I would like to call this action method every 10 seconds to get new data.
Basically need a way to refresh the index page, thereby calling the index method, which gets the latest data from db and display it here.
Can anyone help how to do this in asp.net 4.5 mvc EF code first.
This is a simple mvc application.
Thanks
If you`re sure that it is what you want to do, you can achieve it using jquery:
$(document).ready(function () {
setInterval(function () {
callMyAPI();
}, 10000);
});
function callMyAPI() {
return $.ajax({
url: 'YOUR_URL',
type: "GET",
dataType: "JSON"
});
}

Select2 multiple not working after partialview refresh

I am new to this select2 multiple,
I have a select2 multiple code for selecting multiple in my project as show below,
$('.itemName').select2({
//data: data,
placeholder: 'Select a Language',
ajax: {
url: '/User/LanguageSelect/',
dataType: 'json',
delay: 250,
processResults: function (data) {
//console.log(data);
return {
results: data
};
},
cache: true
}
});
this is in the partrial view, it works fine in the fisrt time but when partial view get refreshed i can not type anything in the box,
i tried adding this line of code too..
$(document).ajaxComplete(function () {
$('.itemName').select2();
});
But when i added this the select2 is not working in the first place either, what did i do wrong??
this is the view i get one first load
First
and after partialview load i get like this
Second
please help
When your partial view is loaded (there could be an event for that) you must (re-)bind all your jQuery objects.
You're on the right track as far as I can see, but the first select2 instantiation is different and besides that I suspect that you're not hooked onto the right event (ajaxComplete).
Can you share how you load your PartialView?
If you don't know any of this; just add this to your PartialView:
<script>
$(function(){
$('.itemName').select2({
// add stuff
});
});
</script>

Ajax request not fired form partial page when the main page also has an ajax request

In my _layout.cshtml I have a partial view that includes js trough requirejs.
<script data-main="/Scripts/Search" src="/Scripts/require.js"></script>
In this js file I use the following to populate a knockout vm.
$.getJSON("/Search/Index", function (data) {
self.availableCities(data.AvailableCities);
});
This works well on all pages except when my main view also has an ajax request.
<script data-main="/Scripts/Index" src="/Scripts/require.js"></script>
$.getJSON("/Property/All", function (data) {
self.properties(data);
});
Here is my require config, it is the same for the partial and the main view.
require.config({
baseUrl: "/Scripts",
paths: {
"text": "text",
"knockout": "knockout-3.3.0",
"jquery": "jquery-2.1.3.min"
},
shim: {
"jquery": { exports: "$" }
}
});
When the main page has an ajax request only this request is fired, I am not sure how to fix this. It looks like a configuration issue, tested it in both Firefox an Chrome so it does not appear to be browser specific.
It turns out having multiple <script data-main="/Scripts/Search" src="/Scripts/require.js"></script> tags in one page isn't such a bright idea.
I figured it out after some more research,
this question has a good solution if you run into a similar problem.
Basically you need one 'main.js' file and add the other page components via the require logic.
Edit:
Doing this may result in the following knockout error:
Error: You cannot apply bindings multiple times to the same element.
To fix this I have used the following binding handler:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
To enable this binding handler on containerless elements use the following:
ko.virtualElements.allowedBindings.stopBinding = true;
Apply the following binding around the partial view. To prevent the main-page from binding to the elements in the partial.
<!-- ko stopBinding: true-->
<!-- /ko -->
Finally use ko.applyBinings on the partialview like this:
ko.applyBindings(new partialViewModel(), document.getElementById("your-partial"));

unwanted multiple ajax request to an action in asp.net mvc

I am developing an asp.net mvc application and have ajax calls on my page. Here is a form which I load by ajax call to page :
The form is located in a partial view
<div id="CreateCultureArea">
<%
using (Ajax.BeginForm("CreateCulture", "Admin", new AjaxOptions() { OnSuccess = "handleCreateCulture" }))
{ %>
.....
<% } %>
</div>
Code Updated
The following script is located in a view :
Create Culture
<script type="text/javascript">
$('.CreateCulture').live('click', function (e) {
e.preventDefault();
var idval = this.id;
$.ajax({
url: "/Admin/CreateCulture",
dataType: 'html',
data: { id: idval },
success: function (mydata) {
$("#CultureContentArea").empty();
$("#CultureContentArea").empty().hide().append(mydata).fadeIn(2000);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
},
type: "GET"
});
return false;
})
</script>
When users click on a link with CreateCulture class, the form is loaded to page. But as I saw the requests in firebug, it calls the action multiple times. I read similar posts like mine on stackoverflow.com and most of them suggested removing repetitive "jquery.unobtrusive-ajax.min.js" calss in page, but as I saw the output page I only see on link to the "jquery.unobtrusive-ajax.min.js" script.
The problem is that #Ajax.BeginForm emits a form that has the attribute data-ajax="true". In the unobtrusive AJAX script (see the non-minified version and look for calls to asyncRequest). So by calling $.ajax yourself, you are repeating the work of #Ajax.BeginForm. Just take out your call to $.ajax and you'll see your action get called only once.
If you need to take action after the AJAX call completes, set the OnComplete property of AjaxOptions in your call to Ajax.BeginForm.
Make sure that you have not multiple .js refrence in page
<script src='/Scripts/jquery.unobtrusive-ajax.min.js'></script>
<script src="/Scripts/jquery.validate.min.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js"></script>
The problem was that I put some scripts in partial view and each time it duplicated by loading this. So I put the scripts all in on page and problem solved.

Display MVC3 Unobtrusive ValidationSummary errors in a jQuery UI Dialog

I'm looking to display MVC3's unobtrusive ValidationSummary errors in a jQuery UI Dialog. Specifically, I want to be able to have a "live" $('.validation-summary-errors').dialog(...);-like experience. That is to say, whenever MVC3 client-side validation would show (for the first time) or update (on repeat offenses) the .validation-summary-errors element, I want the results to appear in a jQuery UI Dialog.
I currently have something along the lines of
#Using Html.BeginForm("Action", "Controller", FormMethod.Post, New With {.id = "MyForm"})
#Html.ValidationSummary()
...
$('#MyForm').submit(function () {
if (!$(this).valid()) {
$('.validation-summary-errors').dialog(...);
return false;
}
});
but this doesn't feel right to me.
It feels like I should be able to hook into the validation framework and be notified that validation completed, and there was an error summary that is now shown or updated with the errors. Then using that event, dialog() the now-shown/updated .validation-summary-errors element. Is there such a thing? Or are there any other suggestions?
So this is how I ended up doing it. I didn't find much documentation, but did enough JS digging to get to this point. Not sure how I feel about it. I do know that I no longer need to hook the form's submit event and "double-up" on the validation calls, so that's good. It just seems that this solution feels "cryptic" (at least in my inexperienced eyes), and I would have expected (and am still looking for) a solution that feels more baked-in.
$(function () {
// If there is an error element already (server side error), show it.
showValidationSummaryDialog();
// When the form validates, and there is an error element, show it
$('#MyForm').bind('invalid-form.validate', function (error, element) {
showValidationSummaryDialog();
}
}
function showValidationSummaryDialog() {
$('.validation-summary-errors').dialog({
title: 'Unable to Save',
close: function () {
$(this).dialog('destroy')
.prependTo($('#MyForm')); // jQuery moves the source element out of the DOM.
// We need to put it back in its place afterwards for validation to maintain its contents.
// TODO: Is there a better way?
}
});
}
If some one want to display both ValidationSummary & ValidationSummaryDialog then try this.
as per #ckittel.
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<div id="ValidationSummary" style="display:none" class="validation-summary-errors">
</div>
}
<script type="text/javascript">
function showValidationSummaryDialog() {
$('#ValidationSummary').html($('.validation-summary-errors').html());
$('#ValidationSummary').dialog({
title: 'Error',
modal: true
});
}
$(document).ready(function () {
$('form').bind('invalid-form.validate', function (error, element) {
showValidationSummaryDialog();
});
});
</script>

Resources