Edge triggers fail when an XmlHttpRequest is aborted - ajax

I was testing a tool that fires some asynchronous ajax requests. The response of one request can abort another one. This works great as far as I have tested this (Chrome, Firefox) but Edge doesn't like aborting! As soon as the XmlHttpRequest is blocked, Edge throws a fail - which I wish not to happen.
This is the code (that gets aborted by another snippet):
xhr = $.ajax("my-url.php")
.done(function(json) {
var data = $.parseJSON(json);
if (data.data) {
// Yay data!
} else {
// Ahw no data or empty data
}
})
.fail(function(jqXHR, textStatus, error) {
// This is triggered by Edge, `error` is "abort"
var string = "An error occurred: " + error + ".";
alert(string);
})
.always(function() {
done = true;
});
So, the result is an alert An error occurred: abort. Question one: why is Edge doing this? Does it handle XHR in a different way than other browsers? What is the standard? Secondly, how do I make sure I do not show this message? Can I simply do something like this in fail(), or isn't that a pretty way of going about this:
if (error != 'abort') {
// Do stuff, only when error isn't abort
}

It seems that I found what happens.
This is my code (copied from yours):
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js" type="text/javascript"></script>
<script type="text/javascript">
'use strict';
var xhr, done;
function getResp() {
if (!done && xhr && xhr.readyState !== 4)
xhr.abort();
done = false;
xhr = $.ajax("json.php",{method:'GET'})
// you didn't include method so method is 'GET'
// when I change it to 'POST' Edge honors xhr.abort()
.done(function (json) {
console.log(json);
var data = $.parseJSON(json);
if (data.data) {
// Yay data!
} else {
// Ahw no data or empty data
}
})
.fail(function (jqXHR, textStatus, error) {
// This is triggered by Edge, `error` is "abort"
var string = "An error occurred: " + error + ".";
//alert(string);
console.log(string);
})
.always(function () {
done = true;
});
}
</script>
</head>
<body>
<input type="button" onclick="getResp()" value="run" />
</body>
</html>
and my php:
<?php
usleep(10000000); //10 seconds
echo '{"name":"test","qty":10,"curr":"'.date('h:i:s').'"}';
?>
General answer: Edge caches xhr GET response and returns data immediately. FF sends request to the server. It makes all difference.
POST requests aren't cached and xhr.abort() produces expected result in all browsers.
Is it acceptable answer?

Related

Post with AngularJS doesn't work

I would like to send a post request to my API. It works with jQuery :
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
$.ajax({
type: "POST",
url: "api.php?option=inscription",
data: {lol : "mess"}
});
</script>
But it doesn't with AngularJS :
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"> </script>
{{1+1}}
<script>
$http.post('api.php?option=inscription', {lol : "mess2"})
.success(function(){alert('cool');});
</script>
If someone can help me. Thank you !
UPDATE :
Thank for your answers, I wanted to simplify but it wasn't clear anymore. So with your help, this is my new code, and the problem is the same. The data in the backend is empty ;
frontend :
<html ng-app="myApp">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"> </script>
<div ng-controller="MainCtrl"></div>
{{data}}
<script>
var app = angular.module('myApp', []);
app.service('SomeService', function($http) {
this.readData = function(dataUrl, dataTobePosted) {
var back = $http.post(dataUrl, dataTobePosted);
back.success(function(data){
console.log(data);
return data;
}).error(function(data, status, headers, config) {
return status;
});
}
});
app.controller('MainCtrl', function($scope, $http, SomeService){
$scope.readData = function(url) {
var dataTobePosted = {"lol": "mess"};
$scope.data = SomeService.readData(url, dataTobePosted);
}
$scope.readData('api.php?option=inscription');
});
</script>
</html>
For clarity, I am suggesting a simple implementation. However, further reading may needed in order to understand the behaviour precisely.
angular.module('myApp').service('SomeService', function($http) {
this.readData = function(dataUrl, dataTobePosted) {
// read data;
return $http.post(dataUrl, dataTobePosted)
.then(function(res) {
return res.data;
}, function(res) {
return res;
}
}
return this;
});
angular.module('myApp').controller('MyController', function($scope, SomeService) {
$scope.readData = function(url) {
var dataTobePosted = {"lol": "mess"};
SomeService.readData(url, dataTobePosted)
.then(function(res) {
$scope.data = res;
}, function(res) {
// Display error
}
}
$scope.readData('api.php?option=inscription');
}
Usage in the HTML page
<div ng-controller="MyController">
{{data}}
</div>
You're using AngularJS as if it's jQuery. It's not. AngularJS works with dependency injection, so you need to wrap your $http call inside a controller.
You should probably read up on AngularJS. A few useful links:
https://docs.angularjs.org/guide/introduction
https://docs.angularjs.org/guide/controller
https://docs.angularjs.org/guide/di
"Thinking in AngularJS" if I have a jQuery background?
My bad, my problem came from my backend in the php I just get my data with :
$data = json_decode(file_get_contents("php://input"));
and not with $_POST

socket.io and differents folders --- solution found

I'm new to socket.io and i already have a problem, minor i think.
I have installed node.js properly and socket.io too with npm. Then just for testing i cut and paste a sample of code from socket.io and everything works well.
Now, i want to strcuture my code and folders and i have created a folder "client" to put a fresh new js file client.js with the client code from the example.
Here is my architecture
/client
client.js
index.html
server.js
client.js :
var socket = io.connect('http://localhost:80');
socket.on('news', function (data) {
alert('sqd');
console.log(data);
socket.emit('my other event', { my: 'data' });
});
server.js
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html', 'utf-8',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html ' + __dirname);
}
res.writeHead(200, {'Content-Type' : 'text/html'});
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
index.html
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
<script type="text/javascript" src="/client/client.js"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
</head>
<body>
</body>
</html>
When i refresh my browser at localhost:80 i have a error on my client.js :
Uncaught SyntaxError: Unexpected token <
Resource interpreted as Script but transferred with MIME type text/html
It seems that there's a problem to interpret my js file as a js file. I've read some threads on the question but nothing works.
Can you help me please ?
Thanx :)
Ok i've found a solution... You have to specify the content type for each file request in a static webserver. May be it could help someone.
Here is the handler function :
function handler (req, res) {
var filePath = req.url;
if (filePath == '/') {
filePath = './client/index.html';
} else {
filePath = './client/lib' + req.url;
}
var extname = path.extname(filePath);
var contentType = 'text/html';
switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
}
path.exists(filePath, function(exists) {
if (exists) {
fs.readFile(filePath, function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
}
else {
res.writeHead(404);
res.end();
}
});
}
Hope this can help someone.
I love to post a problem and respond by myself with no help. Somehow it meens that i'm desesperate too fast. And i love to tell my life in a post too :)
Ok i'm gonna eat something and drink more coffee !!!
Thank you so much! This solved my problem!! And I changed the switch to following code:
var extname = path.extname(filePath);
var contentTypesByExtention = {
'html': 'text/html',
'js': 'text/javascript',
'css': 'text/css'
};
var contentType = contentTypesByExtention[extname] || 'text/plain';
It may be easier to maintain :)
Only that solves:
function handler (request, response) {
var file = __dirname + (request.url == '/' ? '/index.html' : request.url);
fs.readFile(file, function(error, data) {
if (error) {
response.writeHead(500);
return response.end('Error loading index.html');
}
response.writeHead(200);
response.end(data, 'utf-8');
});
}
that's what i need! thank you!
and
we'll add one code line a top of
server.js
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
**, path = require('path')**
You can use mime module as well:
var mime = require('mime')
, content_type = mime.lookup(filePath);
// handle the request here ...
response.setHeader('Content-Type', content_type);
response.writeHead(200);
response.end(data);
And you must made fs.readFile wrapped by a closure, otherwise some file (especially the last file) will be read more than once, and others will not be read at all. And the contentTypewill not be set as you wish. This is because of the callback strategy used by fs.readFile. The problem does not appear when the html file just load one external file, but as the external files(css, js, png) loaded more than one it will appear as i pointed out above. (I came upoon this by myself)
So your code should make a little change as follows:
;(function (filename, contentType) {
fs.readFile(filename, function(err, file) {
// do the left stuff here
});
}(filename, contentType));

Finding the distance between 2 points using Google Maps API

I am trying to find the distance between 2 points, one being from user input and the other an address from my database. I have put together the code below, which seems to work (I have test variables in place so no database pulls are being made for testing), however I have hit a wall; I cannot figure out why I need to click the button twice for the output to show?
Any help is much appreciated
CODE BELOW:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps JavaScript API Example: Extraction of Geocoding Data</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAA7j_Q-rshuWkc8HyFI4V2HxQYPm-xtd00hTQOC0OXpAMO40FHAxT29dNBGfxqMPq5zwdeiDSHEPL89A" type="text/javascript"></script>
<!-- According to the Google Maps API Terms of Service you are required display a Google map when using the Google Maps API. see: http://code.google.com/apis/maps/terms.html -->
<script type="text/javascript">
//var globalAddr = new Array();
var globalName;
var xmlhttp;
var geocoder, location1, location2;
var distanceVal;
function initialize() {
geocoder = new GClientGeocoder();
}
function showLocation() {
geocoder.getLocations(document.getElementById("address1").value, function (response) {
if (!response || response.Status.code != 200)
{
alert("Sorry, we were unable to geocode the first address");
}
else
{
location1 = {lat: response.Placemark[0].Point.coordinates[1], lon: response.Placemark[0].Point.coordinates[0], address: response.Placemark[0].address};
geocoder.getLocations(document.getElementById("address2").value, function (response) {
if (!response || response.Status.code != 200)
{
alert("Sorry, we were unable to geocode the second address");
}
else
{
location2 = {lat: response.Placemark[0].Point.coordinates[1], lon: response.Placemark[0].Point.coordinates[0], address: response.Placemark[0].address};
calculateDistance();
}
});
}
});
}
function calculateDistance()
{
var glatlng1 = new GLatLng(location1.lat, location1.lon);
var glatlng2 = new GLatLng(location2.lat, location2.lon);
var miledistance = glatlng1.distanceFrom(glatlng2, 3959).toFixed(1);
var kmdistance = (miledistance * 1.609344).toFixed(1);
distanceVal = miledistance;
}
function loadXMLDoc(url,cfunc)
{
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function getData(str)
{
loadXMLDoc("getData.php?address="+str,function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
var x = xmlhttp.responseText;
var dnames = x.split("~~~");
var daddr = x.split("^^^");
daddr.shift();
dnames.pop();
var testArray = new Array('85281','18657','90210');
var shortest = 999999;
for(var i = 0; i <= testArray.length-1; i++)
{
document.getElementById("address2").value = testArray[i];//daddr[i];
showLocation();
//i get a blank alert 3 times here the first time, then I get the a value the 2nd time.. makes no sense!
alert(distanceVal);
if (shortest > distanceVal)
{
shortest = distanceVal;
globalName = dnames[i];
}
}
document.getElementById("results").innerHTML = globalName + " " + shortest;
}
})
}
</script>
</head>
<body onload="initialize()">
<form>
<p>
<input type="text" id="address1" name="address1" class="address_input" size="40" />
<input type="hidden" id="address2" name="address2" />
<input type="hidden" id="distance" name="distance" />
<input type="button" name="find" value="Search" onclick="getData(document.getElementsByName('address1')[0].value)"/>
</p>
</form>
<p id="results"></p>
</body>
</html>
When you call showLocation() in your getData() callback, that sets off two geocoder calls and if both are successful calls calculateDistance().
However, both those geocoder calls take time. The first getLocations() sets off a geocode request and lets it continue, to be dealt with in its callback. Within that function, there's another request which is dealt with in its own callback.
While those are waiting for results, the code execution has carried on and reached alert(distanceVal) even though calculateDistance() hasn't been called yet. Consequently distanceVal isn't set yet.
When you click the button again, the global distanceVal will have been populated through all the callback functions, so (even though the second set of geocodes/callbacks have not completed), it will have a value to display. However, if you change the values you are testing, you will find it's displaying the old value which is now incorrect.
Everything which depends on values found in a callback function must be processed within that callback function. If you move the display of data into calculateDistance() everything will be fine, because the data is available to that function.

AJAX call to ASP page to update database record works only once in IE9

After several pieces of helpful advice from the members of this forum I'm getting closer to solving what should be a simple issue. I'm a complete newbie to AJAX.
Can someone please tell me why this piece of code works fine in Firefox 8 but not in IE 9? It works ONCE in IE but not again unless I close the browser and reopen!!??
<script language="javascript" type="text/javascript">
<!--
//Browser Support Code
function ajaxFunction(){
var ajaxRequest;
try {
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
}
catch(e) {
// Internet Explorer Browsers
try {
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e) {
try {
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if (ajaxRequest.readyState == 4) {
}
}
ajaxRequest.open("GET", "disp_processTEXT.asp", false);
ajaxRequest.send(null);
}
//-->
</script>
The readystate is left blank as it is copied code which originally assigned a value to a div but I only want to run the database code in the asp page. Presumably this could have been deleted?
The code to be run in the asp page is
<%
Set MyConn = Server.CreateObject("ADODB.Connection")
MyConn.Open "dsn=xxx;uid=xxx;password=xxx;"
SQLString = "UPDATE dbo_tbl_printing_tempstore SET front_has_text1 = 'YES', front_text = 'help' WHERE id = 106567"
MyConn.Execute(SQLString)
MyConn.Close
Set MyConn = Nothing
%>
Please help me as this is driving me mad.
Many thanks
Switch to jQuery and its Ajax system as it should be cross browser compatable out the box, and saves you huge amounts of time:
Add a reference for jQuery:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
and switch to its Ajax method (place below the above):
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
url: 'disp_processTEXT.asp',
success: function(response) {
alert('It worked');
}
});
});
</script>
Update:
To bind to the onblur event using jQuery try this. Remove the onblur= attribute from your current code.
$(document).ready(function() {
$('#yourinput').on('blur', function(){
$.ajax({
url: 'disp_processTEXT.asp',
success: function(response) {
alert('It worked');
}
});
});
});
Demo: http://jsfiddle.net/qmPLj/
Try this. It has worked for me across all browsers but some Opera versions.
function GetXmlHttpObject(handler)
{
var objXmlHttp=null;
if(navigator.userAgent.indexOf("MSIE")>=0)
{
var strName="Msxml2.XMLHTTP";
if(navigator.appVersion.indexOf("MSIE 5.5")>=0)
{
strName="Microsoft.XMLHTTP";
}
try
{
objXmlHttp=new ActiveXObject(strName);
objXmlHttp.onreadystatechange=handler;
return objXmlHttp;
}
catch(e)
{
alert("Error. Scripting for ActiveX might be disabled");
return;
}
}
else //All other browsers
{
objXmlHttp=new XMLHttpRequest();
objXmlHttp.onload=handler;
objXmlHttp.onerror=handler;
return objXmlHttp;
}
}
var url= "http://www.example.com/";
xmlHttp=GetXmlHttpObject(stateChanged);
xmlHttp.open("GET", url , true);
xmlHttp.send(null);

AJAX Ready State stuck on 1

Hi I can see this has been discussed but after perusing the issues/answers I still don't seem to be able to get even this simple AJAX call to bump out of ready state 1.
Here's the Javascript I have:
<script language="javascript" type="text/javascript">
var request;
function createRequest()
{
try
{
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
}
function loadClassesBySchool()
{
//get require web form pieces for this call
createRequest(); // function to get xmlhttp object
var schoolId = getDDLSelectionValue("ddlSchools");
var grade = getDDLSelectionValue("ddlGrades");
var url = "courses.php?grades=" + escape(grade) + "&schoolId=" + escape(schoolId);
//open server connection
request.open("GET", url, true);
//Setup callback function for server response
//+++read on overflow that some fixed the issue with an onload event this simply had
//+++the handle spitback 2 readystate = 1 alerts
request.onload = updateCourses();
request.onreadystatechanged = updateCourses();
//send the result
request.send();
}
function updateCourses()
{
alert('ready state changed' + request.readyState);
}
function getDDLSelectionValue(ddlID)
{
return document.getElementById(ddlID).options[document.getElementById(ddlID).selectedIndex].value;
}
</script>
The PHP is HERE just a simple print which if i navigate to in the browser (IE/Chrome) loads fine:
<?php
print "test";
?>
I'm quite new at this but seems like I can't get the most bare bones AJAX calls to work, any help as to how work past this would be greatly appreciated.
All I get out of my callback function 'updateCourses' is a 1...
Well after more digging I actually gave up and switched over to jQuery which should for all intents and purposes be doing the EXACT same thing except for the fact that jQuery works... I was just less comfortable with it but so be it.
Here's the jQuery to accomplish the same:
function loadCoursesBySchool(){
var grades = getDDLSelectionValue("ddlGrades");
var schoolId = getDDLSelectionValue("ddlSchools");
jQuery.ajax({
url: "courses.php?grades=" + grades + "&schoolId=" + schoolId,
success: function (data) {
courseDisplay(data);
}
});
}
function courseDisplay(response)
{
//check if anything was setn back!?
if(!response)
{
$("#ddlCourses").html("");
//do nothing?
}
else
{
//empty DLL
$("#ddlCourses").html("");
//add entries
$(response).appendTo("#ddlCourses");
}
}

Resources