grails IE ajax problem - ajax

I'm using grails to develop my application and the 'prototype' library to do an AJAX call with which
I'm having a problem with on IE. In all other browsers my application works fine.
Here is my code:
<html>
<g:form action="ajaxcall" id="recform">
<g:select id="aseselect" name="art" from="${dropdownList}" optionKey="id" optionValue="value" noSelection="['':'- Select -']"/>
<g:submitToRemote action="ajaxcall" value="submit" update="updatediv" />
</g:form>
<div id="updatediv"></div>
</div>
</html>
And this is my controller code:
def ajaxcall = {
String toRender="";
//code that makes db call and adds html into the toRender string
render toRender;
}
The 'toRender' string contains html of an unordered list which renders fine in firefox, chrome and safari but not IE which seems to not get the whole list sometimes or get an empty list some of the times. The behaviour is totally unpredictable depending on IEs mood.
Has anyone come across this issue before? How can I solve this?
Thanks

It's due to IE's caching. I add
response.setHeader("Cache-Control", "no-store")
to controller methods for ajax calls, which tells the browser not to cache that response.
So your controller method should look something like:
def ajaxcall = {
response.setHeader("Cache-Control", "no-store")
String toRender="";
//code that makes db call and adds html into the toRender string
render toRender;
}
There's a more detailed explanation here:
Grails: best way to send cache headers with every ajax call

I fixed the IE (11) browser AJAX using FormData() post to Grails controller problem by removing
< meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
or change it to
< meta http-equiv="X-UA-Compatible" content="IE=Edge" />
in the HTML <head> section.

Related

Ajax Thymeleaf Springboot

I'm trying to use ajax with thymeleaf. I designed a simple html page with two input field. I would like to use addEventHandler for the value of first input text, then I want to send it to controller and make calculation, after that I need to write it in same html form in the second field which returns from controller.
For example:
first input text value -> controller (make calculation) -> (write value) in second input text.
My html page is
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<input type="text" name="Thing" value=""/>
<script th:inline="javascript">
window.onload = function () {
/* event listener */
document.getElementsByName("Thing")[0].addEventListener('change', doThing);
/* function */
function doThing() {
var url = '#{/testurl}';
$("#fill").load(url);
alert('Horray! Someone wrote "' + this.value + '"!');
}
}
</script>
<!-- Results block -->
<div id="fill">
<p th:text="${responseMsg}"/></div>
</div>
</body>
</html>
My controller
#RequestMapping(value = "/testurl", method = RequestMethod.GET)
public String test(Model model) {
model.addAttribute("responseMsg","calcualted value")
return "test";
}
However I cannot call controller from ajax. Could you help me?
There are a few issues with your code. First of all, it looks like you're using the same template for both the initial loading of the application, and returning the calculated result.
You should split these two into different calls if you're using AJAX, since one of the goals of AJAX is that you don't need to reload an entire page for one change.
If you need to return a simple value, you should use a separate request method like this:
#GetMapping("/calculation")
#ResponseBody
public int multiply(#RequestParam int input) {
return input * 2; // The calculation
}
What's important to notice here is that I'm using #ResponseBody and that I'm sending the input to this method as a #RequestParam.
Since you will be returning the calculated value directly, you don't need the Model, nor the responseMsg. So you can remove that from your original request mapping.
You can also remove it from your <div id="fill">, since the goal of your code is to use AJAX to fill this element and not to use Thymeleaf. So you can just have an empty element:
<div id="fill">
</div>
Now, there are also a few issues with your Thymeleaf page. As far as I know, '#{/testurl}' is not the valid syntax for providing URLs. The proper syntax would be to use square brackets:
var url = [[#{/calculation}]];
You also have to make sure you change the url to point to the new request mapping. Additionally, this doesn't look as beautiful since it isn't valid JavaScript, the alternative way to write this is:
var url = /*[[ #{/calculation} ]]*/ null;
Now, your script has also a few issues. Since you're using $().load() you must make sure that you have jQuery loaded somewhere (this looks like jQuery syntax so I'm assuming you want to use jQuery).
You also have to send your input parameter somehow. To do that, you can use the event object that will be passed to the doThing() function, for example:
function doThing(evt) {
var url = [[#{/calculation}]];
$("#fill").load(url + '?input=' + evt.target.value);
alert('Horray! Someone wrote "' + this.value + '"!');
}
As you can see, I'm also adding the ?input=, which will allow you to send the passed value to the AJAX call.
Finally, using $().load() isn't the best way to work with AJAX calls unless you try to load partial HTML templates asynchronously. If you just want to load a value, you could use the following code in stead:
$.get({
url: /*[[ #{/calculation} ]]*/ null,
data: { input: evt.target.value }
}).then(function(result) {
$('#fill').text(result);
});
Be aware that $.get() can be cached by browsers (the same applies to $().load() though). So if the same input parameter can lead to different results, you want to use different HTTP methods (POST for example).

Why does dojo's xhr call to get JSP file content not process the onShow event in InternetExplorer as it does for Firefox and Chrome?

I'm trying to resolve a problem seen only with the IE browser but not Firefox or Chrome. I'm using the dojox.widget.Wizard along with the dojox.widget.WizardPane where each pane in the wizard gets added to the Wizard like so:
nextWizPane = new dojox.widget.WizardPane({
paneId: chosenPaneId,
passFunction: handleNext,
onShow: showingPane
}).placeAt(wizard, newPaneIndex);
loadPaneContents(nextWizPane);
where the loadPaneContents() function dynamically loads the contents for the new WizardPane by using the "dojo/request/xhr" module. The xhr's "then" function accepts the anonymous callback function which accepts the contents of a JSP file which is finally used to set the contents of the WizardPane like so:
xhr(contextPath+"/lib/wizardPanes/UrsPage_"+wizPane.paneId+".jsp",
{sync:true}).then(function(responseFromXhr) {
nextWizPane.set("content", responseFromXhr);
});
where the file that xhr is loading, e.g. UrsPage_1_1_1.jsp, defines the contents for the wizard pane. The contents of the UrsPage_1_1_1.jsp file starts out like:
<%#page session="true" contentType="text/html" pageEncoding="ISO-8859-1"%>
<%#taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<fmt:setBundle basename="/WEB-INF/config/resourcebundle"/>
<div wizardpageidline data-dojo-type="dijit/form/Form" id="1_1_1" encType="multipart/form-data" action="" method="" paneName="Contact Information">
<script type="dojo/on" data-dojo-event="show" data-dojo-args="e">
console.log("UP111 onShow form2");
alert("UP111 onShow form2");
require(
{ packages:[ { name:'lib', location:'/lib' } ] },
["lib/UrsController",
"dojo/on",
"dojo/dom",
"dojo/domReady!"
], function(ursController, dojoOn, dom){
dojoOn(dom.byId("mailingAddressSameAsPrincipal"), "change", function(){
console.log("UP111 changed SameAsMailingAddress this.checked="+this.checked);
alert("UP111 checked Same As box="+this.checked);
});
});
</script>
...<snipped dojo/dijit/HTML markup>
</div>
The log and alert calls above appear when this runs in Chrome and Firefox, but are completely ignored and unexecuted when run in IE8. What am I missing? The JSP file's markup that is snipped does contain dojo/dijit markup that is successfully parsed in both IE, Firefox and Chrome, so it's not like the whole file is being ignored by the dojo parser, just the onShow for the outer . Any help would be appreciated on how to debug this issue.
Thank you,
Gregor
I was faced with this same issue in IE11 and have narrowed it down to this: in an asynch block, it appears that IE has lost scope of the config.contextPath. You may have to get creative on how to gain this value and store it in your widget as a property that can be passed to your asynch call. Example:
var prefix = config.contextPath;
topic.publish("/widget/selected", row.id, prefix); //may need this.prefix depending upon where you gain/store the property and where used.
For my purpose, the code executed fine when IE debug panel or console was open, but failed when console was closed. Alert statements were the only thing that could aid in this matter. Basically, confirm the url you are building before executing the ajax

Going back to a page gives the original page

If i feed a page to a user, e.g.:
<!DOCTYPE HTML>
<HTML>
<HEAD>
<SCRIPT type="text/javascript">
function cbCountry_Click()
{
var select = document.getElementById("cbCountry");
select.options[select.options.length] = new Option("Canada", "CA");
select.options[select.options.length] = new Option("United States", "US");
}​
</SCRIPT>
</HEAD>
<BODY>
<SELECT id="cbCountry"></SELECT>
<P><BUTTON onclick="cbCountry_Click()">Get Countries</BUTTON>
<P>Visit a link
</BODY>
</HTML>
This page has the capability to modify itself. In this case it's standalone javascript, but imagine it's AJAX code.
If the user then clicks a link to go forward, then clicks to return back to this page; they will be presented with the page as it is cached; rather than how they left it:
Has anyone solved the AJAX problem?
i notice that Google periodically sends the state of your page to the server. If you return to the page they force a timer refresh, which refreshes the state of the page from the server.
i've also noticed that some people simply force the page to not be cached:
Cache-Control: nocache
preventing the page from being cached. Downside of that is that it prevents the page from being cached.
You could use a cookie to store the state on the client side and restore the state of the page from the cookie whenever your page is loaded.

How to Return a View from a Controller to an iFrame

I am new to MVC 3 and have come accross the following scenario:
First let me explain how I have setup my application:
All post backs to the server use jquery ajax which which return a view from the controller that either appended or prepended or replace a targeted div.
The Scenario:
I have come to a point where I would like to upload images but unfortunately because of the jquery ajax posting I cannot get the values for an html in C# Request.Files. I know there are plugins out there to help out with this but I would like to do this myself so I have created an <iframe> which i then use a bit of javascript and post the form targeted to the iframe (old classic way of doing things):
function UploadImg(SACTION) {
alert(SACTION);
validatorform.action = SACTION;
validatorform.target = "fraImage";
validatorform.method = "POST";
validatorform.submit();
}
the SACTION parameter looks like this #Url.Action("UploadFile", "Response"). This all works well as it hits the controllers action method and I can then save the image:
[HttpPost]
public ActionResult UploadFile(string ArticleID)
{
ViewBag.PreviewImage = cFileUploads.UploadFile(ArticleID, Request.Files[0]);
return View("ImagePreview");
}
I would now like to return a view to the iframe (simply to preview the image and then do a couple of other things but this is besides the point)
The View for previewing the Image:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="">
<img alt="" src="#ViewBag.PreviewImage" />
</form>
</body>
</html>
The Problem:
Unfortunately when I return the View (ImagePreview.cshtml) in C# the whole page is refreshed, all I want is for the iFrame to be refreshed. How should I return the view from the controller?
Fiqured out the problem, I had a bit of javascript that was replacing the contents of a div that the iframe was sitting in ... (slap on my forehead). All working perfectly now :)
Will leave this question here just incase ..

Using MVC3, how to get browsers to explicitly interpret a transferred script as HTML?

In my MVC app, I am returning some Javascript. Howveer, I am using the anti-forgery token on the view, so the rendered result would be
<input name="__RequestVerificationToken" type="hidden" value="E8as+4Ff1u/c/+kuFcNXXCREB5pz5GAfH2krN5RvzURJaHZSApuRc4czZqmoITaKdy0XhN5sFfRzl4ne+wB3PkWOscBWzoIxUk3hGaFwDxRXSbMs8K9IwojEAtV5u57MR7hiSujr6MOTpjjbf5FPaYgO4gmH6lSR9mbSyO2IedI=" />
<script type="text/javascript">
// Here, we ensure that jQuery is loaded then load up the rest of our JS in in order.
ord = Math.random() * 10000000000000000;
...
So there is some HTML to be added to the page then the JS.
The issue is that I get the following notification in Chrome:
Resource interpreted as Script but transferred with MIME type
I need the browser to interpret this as HTML in order to make use of the anti-forgery token.
I have tried putting this on the view:
<%#Page Title="" Language="C#" ContentType="text/xml" %>
Which renders:
<%System.Web.WebPages.DynamicPageDataDictionary`1[System.Object] Title="" Language="C#" ContentType="text/xml" %>
<input name="__RequestVerificationToken" type="hidden"
...
...but the same message persists.
In my controller I have also tried:
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(page.clientScript);
return new ContentResult
{
ContentType = "text/xml", // also tried text/html
Content = Encoding.UTF8.GetString(bytes),
ContentEncoding = System.Text.Encoding.UTF8
};
Same issue.
-- UPDATE --
This is how I'm invoking the MVC app to return the text:
// used to load scripts on to the client script using a non-blocking asynch request
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://myserver/MyAppPath/someids';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
If I've understood correctly, you need an MVC action which returns both html and a script tag that can be injected in a page via a <script... include. You also want to render this via an MVC view.
The biggest issue you've missed is that in order to get this content into the calling page, you need to execute document.write from the script - you can't just send back HTML and script in response to the script include - the browser won't understand it, it's expecting javascript only.
There are a few ways to do this - I have written a full suite of ViewContent MVC controller methods, with the same overloads as View which returns the result of a view to a controller action as a string. I can then pass that back as a string literal (useful for html email generation) but also to a javascript encoder.
In this case, you don't need to be so generalist. We can leverage Darin Dimitrov's answer to this SO: Embed MVC Partial View into a document.write JS call and split your view into a View and a partial. The view writes the document.write() skeleton, and the partial view renders the dynamic html you want to be injected into the page. It's unclear if you're using the Anti Forgery Token in the main view which will call the script (in which case it should be rendered as part of the view that it returns) or if you're actually hard-coding it in the script. The second should definitely not be used but I'm writing this answer as if it is, because that appears to be what you want.
First, your partial view (let's call it Fragment.cshtml, put it in ~/Views/Shared)
<input name="__RequestVerificationToken"
type="hidden"value="[ommitted]" />
<script type="text/javascript">
// Here, we ensure that jQuery is loaded then load up the rest of our JS in in order.
ord = Math.random() * 10000000000000000;
...
Second, the host view, called SomeIds.cshtml
#{ Response.ContentType = "text/javascript"; }
document.write('#Html.Raw(HttpUtility.JavaScriptStringEncode(Html.Partial("~/Views/Shared/Fragment").ToHtmlString()))')
Now this view returns a document.write call that injects the HTML returned by the Fragment.cshtml into the page that includes the script.
Are you returning a PartialView that has all of the markup rendered?
Create a PartialView with your (form and script includes) and in your Controller:
public ActionResult Index(Models.MyModel model)
{
// validate the model if needed
return PartialView("[My PartialView Name]", model);
}
You could put your scripts in separate files, and add the [script src] tags in the PartialView.

Resources