I am developing React application and for frontend AJAX requests I use jQuery, but I want to cache my requests like angular http.get(url, {cache: true }) does.
Is there any way which can help me do this global caching for GET requests.
I tried to add cache: true property to request but it seems not working.
For example my code looks like this
$.ajax(source, {
method: 'GET',
data: {
c: count,
p: period
},
cache: true,
success: (response) => {
}
})
I have tried also
$.ajaxSetup({
cache:true
});
for all requests, but unfortunatley I can see request under Chrome devtools network tab, as well as in my server logs.
So I want to prevent from doing same request if data and url is same.
I can create some storage myself, but I think there should be default way for doing this.
Thanks in advance!
One approach could be checking if the last request data are the same than the current.
var lastRequestedData = {};
function myAjaxRequest(requestData) {
if (JSON.stringify(requestData) != JSON.stringify(lastRequestedData)) {
lastRequestedData = requestData;
alert('Makes the ajax request: '+ JSON.stringify(requestData));
//$.ajax(...)
}
}
myAjaxRequest({"c":1,"p":2}); // Fire
myAjaxRequest({"c":1,"p":2}); // Not fire
myAjaxRequest({"c":2,"p":3}); // Fire
Related
Issue:
The first ajax is working properly in the main.js, the second one is doing its job at first look but I think there might be a bug somewhere. I can reach the getProducts method after I click to the button.
The product_list.html file should appear on the browser screen, but it doesn't.
I get no error message on the front-end or the back-end.
This is what I noticed: After click to the button -> F12 -> Network -> products -> I can see here a status code: 200 and the product_list.html file content as response.
In case the POST ajax call succeeds and in the case I add: location.href = "/products";, the browser will load product_list.html
I use the get ajax call because i need to pass the jwt token in the req header. (I deleted the jwt authentication parts from the code below because I narrowed down the error to the $.ajax() and res.sendFile() relationship)
//routes.js
routes.get("/products", ProductController.getProducts);
//ProductController.js
var root = path.join(__dirname, '../../views');
module.exports = {
getProducts(req, res){
console.log("getProducts!"); //I can see in the console
res.sendFile("product_list.html", {root}) //It doesn't render the html
},
}
//main.js
$("#btn-login").click(function(){
$.ajax({
type: 'POST',
url: "http://localhost:8000/login",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({
"username": $("#login-user").val(),
"password": $("#login-pwd").val(),
}),
success: function(data){
if ($("#login-chkbx").is(':checked')){
$.ajax({
url: "http://localhost:8000/products",
type: 'GET',
beforeSend: function (xhr) {
xhr.setRequestHeader("user", localStorage.getItem("user"));
},
});
}
}else{
console.log("Checkbox is not checked");
}
}
});
});
What causes the issue and how to solve it?
Thanks!
file should appear on the browser screen
No it does not and it should not. The file should be returned to the ajax function call in the success callback:
$.ajax({
url: "http://localhost:8000/products",
type: 'GET',
beforeSend: function (xhr) {
xhr.setRequestHeader("user", localStorage.getItem("user"));
},
success: function (file) {
// The file is in the "file" variable above.
// Do whatever you want with it. For example you can replace
// the current page with the returned file:
document.body.innerHTML = file;
}
});
That is the whole point of AJAX - a mechanism for programmers to override the normal flow of HTTP requests that loads the response to the browser page. If it allows you to not load the response to the browser page it also means it will not automatically load the response to the browser page - because doing so will not allow you to not load the response.
If you want to automatically load the response then don't use ajax:
// Replace $.ajax(... with:
window.location.href = "http://localhost:8000/products";
However, this method does not allow you to set custom request header.
in your frontend code you do nothing with the GET /products response
the backend sendfile as the name says it just sends the file over to the requester. Being an ajax call, it must be rendered by the frontend code.
Add something like success: response => $('body').html(response)
I've an array of items in javascript, and for each item, I've to make an ajax post to a URL to get corresponding info and display the info in a container. The code broadly looks like this:
var data = some_data;
array.forEach(item, idx)=> {
callAjaxAndUpdate(data, item, $('div#container'+i);
});
and the Ajax method is simply
var standardUrl = 'https://example.com/post.php';
function callAjaxAndUpdate(data, item, container) {
$.ajax({
url: standardUrl
data: data,
type: 'POST',
}).done(function(res) {
container.append(res.data);
}).fail(function(res) {
container.append(res.responseText);
}).always(function() {
container.append('DONE!');
});
}
However, this thing whole thing seems to have become blocking. I did server-side logging of timestamps, and I could see that ajax request for each item is getting triggered only after that for the previous one has completed (always section executed).
Could someone help me out with why this setup is synchronous, and how to make it async? Please note that this script is executing in the browser, and not on nodeJS.
EDIT: Turns out, it's the PHP backend which is processing the requests in a blocking way. Perhaps the session is the culprit here. I would close the question now. Thanks for your help and suggestions.
Try default Ajax async
var standardUrl = 'https://example.com/post.php';
function callAjaxAndUpdate(data, item, container) {
$.ajax({
url: standardUrl,
async: true,
data: data,
type: 'POST',
}).done(function(res) {
container.append(res.data);
}).fail(function(res) {
container.append(res.responseText);
}).always(function() {
container.append('DONE!');
});
}
Or you can call the method on .done method
Inside my controller I have this function for the route /backups
public function index()
{
$backups = \App\Backup::all();
if(request()->ajax()) {
return $backups;
}
return view('backups.index', compact('backups'));
}
My idea was that if I have my javascript ask for the data then return json if not return html.
This works fine, except when pressing the browser back button to go from lets say /backups/1 to /backups it shows the json.
Is there another function I can use that will only respond to ajax calls from my code and not the browsers?
I'd recommend adding an ajax-only query string parameter to the ajax request, e.g. ?ajax=1.
This way, you can 1. utilise the browser cache, and 2. keep the same Laravel route for both request types.
Make sure your AJAX requests use a different URL from the full HTML documents. Chrome (and most probably Firefox) caches the most recent request even if it is just a partial.
Source:
https://code.google.com/p/chromium/issues/detail?id=108425
Or:
Try setting cache to false
$.ajax({
dataType: "json",
url: url,
cache: false,
success: function (data) {...}
});
Question about store data population in isomorphic flux apps. (I'm using react, alt, iso and node but theory applies to other examples)
I have a flux 'store' (http://alt.js.org/docs/stores/) that needs to get data from an api:
getState() {
return {
data : makeHttpRequest(url)
}
}
and as the user navigates through the SPA, more data will be loaded via http requests.
I want this app to be isomorphic so that I can render the apps full html including latest data server side and return it to the user for fast initial page load.
react.renderToString() lets me render the app as html, and I can seed the data using alt&iso like:
storeData = { "MyStore" : {"key" : "value"}}; // set data for store
alt.bootstrap(JSON.stringify(storeData || {})); // seed store with data
var content = React.renderToString(React.createElement(myApp)); // render react app to html
The problem is that I will see errors when running the js server side as the store will want to make a http request which it wont be able to do (as xmlhttprequest wont exist in node)
Whats the best way to solve this problem?
The only solution I can think of would be to wrap the httprequest from the store with:
var ExecutionEnvironment = require('react/lib/ExecutionEnvironment');
...
if (ExecutionEnvironment.canUseDOM) {
// make http request
} else {
// do nothing
}
Any better ideas? Thanks in advance.
I would recommend hooking into your Ajax library or XMLHttpRequest directly if you are running serverside. Just shim it with code that supplies data directly from your database or application.
A quick example:
var noop= function(){}
window.XMLHttpRequest= function(){
console.log("xhr created", arguments);
return {
open: function(method, url){
console.log("xhr open", method, url);
// asynchronously respond
setTimeout(function(){
// pull this data from your database/application
this.responseText= JSON.stringify({
foo: "bar"
});
this.status= 200;
this.statusText= "Marvellous";
if(this.onload){
this.onload();
}
// other libs may implement onreadystatechange
}.bind(this), 1)
},
// receive data here
send: function(data){
console.log("xhr send", data);
},
close: noop,
abort: noop,
setRequestHeader: noop,
overrideMimeType: noop,
getAllResponseHeaders: noop,
getResponseHeader: noop,
};
}
$.ajax({
method: "GET",
url: "foo/bar",
dataType: "json",
success: function(data){
console.log("ajax complete", data);
},
error: function(){
console.log("something failed", arguments);
}
});
http://jsfiddle.net/qs8r8L4f/
I whipped this up in the last 5 minutes mostly using the XMLHTTPRequest mdn page
However if you are using anything not directly based on XMLHttpRequest or explicitly node aware (like superagent) you will probably need to shim the library function itself.
Other work to do on this snippet would be implementing errors and different content types.
I know that Firefox and jQuery don't allow sync requests with credentials. My problem is that I need to know if the user is logged in or not (by checking his session on the server by hitting an /echo endpoint).
Otherwise my Backbone application enters a bad state because when I navigate to a backbone route like #newActivity there is this logic in my controller:
if (Session.status!=Session.LOGGED_IN) {
Backbone.history.navigate('login', true);
return;
}
Session is a singleton object. In there if I hit async the /echo endpoint, until the time success is called my controller already thinks that we are not logged in.
Outcome: when the user refreshed the page he is always redirected in the login prompt.
If I make the request sync everything works ok with Chrome.
The request is:
$.ajax({
url: Config.serverUrl + "/rest/auth/echo",
type: 'GET',
dataType: "json",
async:false,
xhrFields: {
withCredentials: true
},
/* beforeSend: function(xhr) {
xhr.withCredentials = true;
},*/
crossDomain: true,
success: function (data) {
self.bootstrap(data)
self.globalCh.vent.trigger('loggedIn',data.username);
}
});
I kind of understand why they want to phase out sync queries, but sometimes they are necessary for not having an unstable application state.
Any workarounds or ideas about a better implementation?
you need to use promises and/or deferred objects.
http://api.jquery.com/category/deferred-object/
jQuery's AJAX call returns a promise. That means you can fire a callback whereever you want to when the ajax call is finished.
add a return to your ajax request.
function getLoginInformation() {
return $.ajax({
// your ajax call to check if the user is logged in or not
});
}
and then on page load, you fire the stuff that needs the login information AFTER you have it. Like this:
getLoginInformation().then(function(data) {
// load the app and everything else that needs credentials
});