Ajax.ActionLink not working, Response.IsAjaxRequest() is always false - asp.net

I have been googling/SO:ing this issue for a while and many seem to be sharing this, but I haven't found any successful solution to my problem.
Using MVC3 and Razor.
Master page contains:
<script src="#Url.Content("~/Scripts/jquery-1.5.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
AjaxTest.cshtml contains:
<div id="AjaxTestDiv">content</div>
#Ajax.ActionLink("Update", "AjaxTester", new AjaxOptions { UpdateTargetId = "AjaxTestDiv" })
AjaxTester action method:
public string AjaxTester()
{
if (Request.IsAjaxRequest())
{
return DateTime.Now.ToString();
}
else
{
return "FAIL";
}
}
I always get the "FAIL" returned, to a blank page, not in the targeted div.
Edit: Also note that if I remove the if (Request.IsAjaxRequest()), I still don't get back anything to the targeted div, but instead a blank page.
Edit2: Looking at the HTML generated, this is my link:
<a data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace"
data-ajax-update="#AjaxTestDiv" href="/Area/AjaxTester">Update</a>
Have tried switching the method to GET, to no avail.

By default ASP.NET MVC 3 uses unobtrusive jquery with all the Ajax.* helpers. So start by getting rid off all MicrosoftAjax scripts (this useless c**p) and put the following instead:
<script src="#Url.Content("~/Scripts/jquery-1.5.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
and then simply activate unobtrusive AJAX in your web.config (if not already done):
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
Now jquery is going to unobtrusively AJAXify all the links containing those HTML 5 data-* attributes.
Or even better IMHO:
In your view simply:
#Html.ActionLink("Update", "AjaxTester", new { id = "mylink" })
and in a separate javascript file AJAXify this anchor:
$(function() {
$('#mylink').click(function() {
$('#AjaxTestDiv').load(this.href);
return false;
});
});

Another IE-specific issue that can keep ActionLink from functioning correctly is covered here: ASP.NET MVC - Prevent Cache on Ajax.ActionLinks
Basically, IE sometimes caches Ajax GET requests, so setting the AjaxOption's HttpMethod to POST is a less fragile way to construct an ActionLink:
#Ajax.ActionLink(
item.Name + " (Ajax link test)",
"MyActionName",
routeValues: new { id = item.Id },
ajaxOptions: new AjaxOptions()
{
UpdateTargetId = "divToUpdate",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST"
})

For those still facing this issue:
You should have below files in your project somewhere:
If not, below is the nuget package, either download this from nuget manager in your visual studio project or package manager console:
https://www.nuget.org/packages/Microsoft.jQuery.Unobtrusive.Validation/3.2.11/
Give reference of above added file after the jquery.min.js latest version in either your respective view or layout file:

Add the jquery.unobtrusive-ajax.js package in your project and it should work in the expected way.
Include the current script form
CDN: https://www.cdnpkg.com/jquery-ajax-unobtrusive
<script src="#Url.Content("https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
or install package
NuGet:
https://www.nuget.org/packages/Microsoft.jQuery.Unobtrusive.Ajax/3.2.6
and apply installed bundle.
Note that jquery.unobtrusive-ajax.js and jquery.validate.unobtrusive.js are not equal.
In my case this package was not installed and ajax.action link worked like html.actionLink and rendered entire view instead of inserting partial view in the target div.

Related

Asp.Net Mvc Bundles and Specific Views

I use DataTables in some specific tables and it needs at least 10 different js/css files to work in my situation. So i tried to put them in a bundle and called that bundle in that view. But no success.
My question is that: Are bundles only for entire site (loads in every page) or can i use some of them in specific views only?
BundleConfig
bundles.Add(new ScriptBundle("~/Content/DataTablesJS").Include("~/Content/DataTables/*.jss"));
bundles.Add(new StyleBundle("~/Content/DataTablesCSS").Include("~/Content/DataTables/*.css"));
View
#section Styles
{
#Scripts.Render("~/Content/DataTablesCSS")
}
#section Scripts
{
#Scripts.Render("~/Content/DataTablesJS")
<script src="~/Content/SayfaJSs/DataTables.jss" type="text/javascript"></script>
<script>
jQuery(document).ready(function() {
DataTables.init();
});
</script>
}
HTML Output
<script src="/Content/DataTablesCSS?v=z-Ctaq2TbplDFpORl0e9NGH8TjpB5hQ2cPam2OxmDEo1"></script>
<script src="/Content/DataTablesJS?v=2gXGKlcqr0bFFqv6Bbr9jB_7LVvvHDrghzBwHCgFJds1"></script>
In your layout you would have something like this:
#RenderSection("Scripts", required: false)
...Then in your view you would add your bundle reference like this:
#section Scripts {
#Scripts.Render("~/bundles/yourBundle")
}
I found the problem. It was using asterisk to get all scripts in folder. From asp.net website:
"Adding scripts by wildcard defaults to loading them in alphabetical order, which is typically not what you want. "
My scripts needs a special order. So i included them in order that i need:
bundles.Add(new StyleBundle("~/Content/DataTablesCSS")
.Include("~/Content/DataTables/dataTables.bootstrap.css",
"~/Content/DataTables/buttons.dataTables.min.css",
"~/Content/DataTables/select.dataTables.min.css"));
bundles.Add(new ScriptBundle("~/Content/DataTablesJS")
.Include("~/Content/DataTables/jquery.dataTables.min.js",
"~/Content/DataTables/dataTables.bootstrap.js",
"~/Content/DataTables/dataTables.buttons.min.js",
"~/Content/DataTables/dataTables.select.min.js",
"~/Content/DataTables/buttons.bootstrap.min.js",
"~/Content/DataTables/jszip.min.js",
"~/Content/DataTables/pdfmake.min.js",
"~/Content/DataTables/vfs_fonts.js",
"~/Content/DataTables/buttons.html5.min.js",
"~/Content/DataTables/buttons.print.min.js"));

Dust.js - Render precompiled anonymous template on client side

Is there a way to render a precompiled template that has no name on the client side in DustJs?
Because documentation only shows with a name:
<!-- precompiled templates -->
<script type="text/javascript" src="/lib/templates.js"></script>
<script type="text/javascript">
// The templates are already registered, so we are ready to render!
dust.render('hello', { world: "Saturn" }, function(err, out) {
document.getElementById('output').textContent = out;
})
</script>
EDIT : Ok it's probably too complicated to load a file, and I just noticed that when we compile without specifying name (in order to compile many template simultaneously), the path of the template is set as the default name. It is even editable with --pwd flag.
There is therefore always a name so the above function can operate.
It sounds like you would like to load templates by their path after they have been precompiled. Dust allows you to do this via AMD (require.js) compatibility.
http://www.dustjs.com/guides/setup/#amd
Once you've loaded require.js and set define.amd.dust = true, you can call dust.render with the path to a template and Dust will automatically load it for you.
Note that this requires that you compile your templates with the --amd flag.
<script src="r.js"></script>
<script type="text/javascript">
define.amd.dust = true;
require(["lib/dust-full"], function(dust) {
dust.render('path/to/your/template', function(err, out) { ... });
});
</script>
The Dust repository has an example of using AMD to load templates.

Excluding bootstrap from specific routes in Meteor

I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)

CKEditor plugin for ASP.NET, 'ExtraPlugins' property doesn't work

I have two ASP.NET Web Forms Apps, first targeted to v4.0, second to v4.5 of .NET framework. I use ASP.NET CKeditor plugin v3.6.4. Everything work great, except that I can't register my 'phrases' plugin using 'ExtraPlugins" property.
Javascript solution works:
CKEDITOR.replace('<%=CKEditor1.ClientID%>',
{
extraPlugins: 'phrases',
toolbar:
[
['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink'],
['phrases']
]
});
But 'ExtraPlugins' property solution doesn't work:
<CKEditor:CKEditorControl ID="CKEditor1" BasePath="~/ckeditor" ExtraPlugins="phrases" Toolbar="Basic" runat="server" />
Please for help.
Best regards,
WP
I have done some experiments and finally I know how it works.
The following ASP.NET CKEditor Web Control tag is placed in *.aspx page:
<CKEditor:CKEditorControl ID="CKEditor1" BasePath="~/ckeditor" ExtraPlugins="phrases" Toolbar="Basic" runat="server" />
Then, this tag is rendered in the outcoming HTML document and shipped to the user browser in that way:
First part:
<script type="text/javascript">
//<![CDATA[
function WebForm_OnSubmit() {
CKEditor_TextBoxEncode('CKEditor1', 0);
return true;
}
//]]>
</script>
Second part:
<textarea name="CKEditor1" rows="2" cols="20" id="CKEditor1">
</textarea>
Third part:
<script type="text/javascript">
//<![CDATA[
var CKEditor_Controls=[],CKEditor_Init=[];function CKEditor_TextBoxEncode(d,e){var f;if(typeof CKEDITOR=='undefined'||typeof CKEDITOR.instances[d]=='undefined'){f=document.getElementById(d);if(f)f.value=f.value.replace(/</g,'<').replace(/>/g,'>');}else{var g=CKEDITOR.instances[d];if(e&&(typeof Page_BlockSubmit=='undefined'||!Page_BlockSubmit)){g.destroy();f=document.getElementById(d);if(f)f.style.visibility='hidden';}else g.updateElement();}};(function(){if(typeof CKEDITOR!='undefined'){var d=document.getElementById('CKEditor1');if(d)d.style.visibility='hidden';}var e=function(){var f=CKEditor_Controls,g=CKEditor_Init,h=window.pageLoad,i=function(){for(var j=f.length;j--;){var k=document.getElementById(f[j]);if(k&&k.value&&(k.value.indexOf('<')==-1||k.value.indexOf('>')==-1))k.value=k.value.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');}if(typeof CKEDITOR!='undefined')for(var j=0;j<g.length;j++)g[j].call(this);};window.pageLoad=function(j,k){if(k.get_isPartialLoad())setTimeout(i,0);if(h&&typeof h=='function')h.call(this,j,k);};if(typeof Page_ClientValidate=='function'&&typeof CKEDITOR!='undefined')Page_ClientValidate=CKEDITOR.tools.override(Page_ClientValidate,function(j){return function(){for(var k in CKEDITOR.instances){if(document.getElementById(k))CKEDITOR.instances[k].updateElement();}return j.apply(this,arguments);};});setTimeout(i,0);};if(typeof Sys!='undefined'&&typeof Sys.Application!='undefined')Sys.Application.add_load(e);if(window.addEventListener)window.addEventListener('load',e,false);else if(window.attachEvent)window.attachEvent('onload',e);})();CKEditor_Controls.push('CKEditor1');
CKEditor_Init.push(function(){if(typeof CKEDITOR.instances['CKEditor1']!='undefined' || !document.getElementById('CKEditor1')) return;CKEDITOR.replace('CKEditor1',{"extraPlugins" : "phrases", "htmlEncodeOutput" : true, "toolbar" : "Basic"}); });
//]]>
</script>
In the third part of source code, there is the most important statement, which is rendered equivalent of ASP.NET CKEditor tag:
CKEDITOR.replace('CKEditor1',{"extraPlugins" : "phrases", "htmlEncodeOutput" : true, "toolbar" : "Basic"}); });
As we can see, ASP.NET WebControl's property ExtraPlugings is linked with extraPlugins property in Javascript configuration of CKEditor instance.
After some recognitions it turns out that JS configuration extraPlugins option actually doesn't attach plugin to be visible in CKEditor toolbar and ready to use, but in fact only register the plugin to allow to use it. Plugin can be registered in that way, or in config.js configuration file of CKEditor:
CKEDITOR.editorConfig = function( config )
{
config.extraPlugins = 'phrases';
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
};
After registration, the plugin have to be somehow added to the toolbar to be available for use, for example in that manner:
<CKEditor:CKEditorControl ID="CKEditor1" BasePath="~/ckeditor" ExtraPlugins="phrases" Toolbar="[{ name: 'plugins', items: ['phrases'] }]" runat="server" />
or by use of Javascript Code.
Thus, in summary: ExtraPlugins property only cause a registration of plugin. If we want to have it on CKEditor Toolbar, we have to write proper statements to configure CKEDitor toolbar.

MVC Mini Profiler includes not respecting application's path

I've got the MVC Mini Profiler set up as described on its project page, and the includes are indeed being written on the page.
Problem is, my application sits at http://localhost:8080/web, and the markup written by the profiler includes looks like this:
<link rel="stylesheet/less" type="text/css" href="/mini-profiler-includes.less?v=2.0.4177.17902">
<script type="text/javascript" src="/mini-profiler-includes.js?v=2.0.4177.17902"></script>
<script type="text/javascript"> jQuery(function() { MiniProfiler.init({ id:'fb4dc30e-c1aa-4be6-902c-ef2812dd1fe2', renderDirection:'left' }); } ); </script>
These all of course give 404 errors, but if I navigate to /web/mini-profiler-includes.less?, it loads fine.
The source that creates that string can be found here:
// MiniProfilerHandler.cs
/// <summary>
/// Understands how to route and respond to MiniProfiler UI urls.
/// </summary>
public class MiniProfilerHandler : IRouteHandler, IHttpHandler
{
internal static HtmlString RenderIncludes(MiniProfiler profiler, RenderPosition? position = null, bool showTrivial = false, bool showTimeWithChildren = false)
{
const string format =
#"<link rel=""stylesheet/less"" type=""text/css"" href=""{0}mini-profiler-includes.less?v={1}"">
<script type=""text/javascript"" src=""{0}mini-profiler-includes.js?v={1}""></script>
<script type=""text/javascript""> jQuery(function() {{ MiniProfiler.init({{ id:'{2}', path:'{0}', renderDirection:'{3}', showTrivial: {4}, showChildrenTime: {5} }}); }} ); </script>";
var pos = position ?? (MiniProfiler.Settings.RenderPopupButtonOnRight ? RenderPosition.Right : RenderPosition.Left);
var result = profiler == null ? "" : string.Format(format,
EnsureEndingSlash(HttpContext.Current.Request.ApplicationPath),
MiniProfiler.Settings.Version,
profiler.Id,
pos.ToString().ToLower(),
showTrivial ? "true" : "false",
showTimeWithChildren ? "true" : "false");
return new HtmlString(result);
}
// rest of the code
}
Why isn't Request.ApplicationPath returning my application's path? Am I doing something wrong, or should I file an issue on the mvc-mini-profiler page?
EDIT: To make things even weirder, I put a breakpoint on the MiniProfiler.RenderIncludes() call, and checked what the value of HttpContext.Current.Request.ApplicationPath was at that moment, and it was "/web"! Very mysterious.
EDIT 2: Looks like they may have added support for virtual paths in the latest version (2 hours ago :)), and the NuGet package (which is how I installed it) is not completely up to date. Investigating...
Pulling the latest source (this commit being the most recent at time of this post), building the project, grabbing the DLL and referencing that instead of using the project's NuGet package fixed the problem.
EDIT: As of right now, the NuGet package is now up to date with the latest commit, so NuGet away!

Resources