Ajax Requests Slow in Django - javascript

I have the following ajax request in my django template:
$('#subjects').on('change', function() {
var subject = $('#subjects').find(":selected").text();
$.ajax({
type: "GET",
url: "/classes/" + term + "/" + subject , // or just url: "/my-url/path/"
dataType: "html",
success: function(data) {
$('#classes').html(data);
}
});
//remove courses
//remove overview
//get courses for specified subject
//put them under course
});
The "subject" id is for a select form like this:
<select size="7" class="form-control" id="subjects">
{% for subject in subjects %}
<option>{{ subject }}</option>
{% endfor %}
</select>
So, when a subject is changed, I send an ajax request to the server so that I can get the classes for that subject from the database (as there are thousands of classes). However, this takes ~1 second. If a user simply arrows down my list of subjects, then tens of ajax requests will be fired off in a second, causing a backup and slowing down the data being displayed properly.
I tried aborting all previous ajax requests before sending another one, but the problem is the server will still process these, so it did not fix the problem.
Is there any way to speed this up, and somehow only take 1 second any time a user scrolls down to a subject? Or, is there another method that anyone would recommend?
Follow up question. Another way I just thought of would be to only send the ajax request if an option is selected for longer than 1 second. this would make it take 2 seconds which is fine. Is there a way to do this?

Answering to your follow up question, here is a jQuery function that allow to delay the callback of an event a given amount of milliseconds:
(function ($) {
$.fn.delayOnEvent = function(onevent, callback, ms){
$(this).on(onevent, function( event ){
var srcEl = event.currentTarget;
if( srcEl.delayTimer )
clearTimeout ( srcEl.delayTimer );
srcEl.delayTimer = setTimeout(function(){ callback( $(srcEl) ); }, ms);
});
return $(this);
};
})(jQuery);
You can call it this way in your case:
$('#subjects').delayOnEvent('change', function() {
...
}, 1000); // one second delay

Do something like this enable/disable
$('#subjects').on('change', function() {
var subject = $('#subjects').find(":selected").text();
document.getElementById('subjects').disabled=true
$.ajax({
type: "GET",
url: "/classes/" + term + "/" + subject , // or just url: "/my-url/path/"
dataType: "html",
success: function(data) {
$('#classes').html(data);
document.getElementById('subjects').disabled=false
}
});
#rest of code

Related

AJAX query not always updating information consistently

I am experiecing some issues with AJAX updating the page. The actual data in the database is updated but this is not always reflecting in real time on the web page.
For example, I have the following event:
$("#add_note").click(function(e) {
//e.preventDefault();
$("#add_note_form").validate({
rules: {
contact_note: {
required: true
}
},
submitHandler: function(form) {
contact.modal_update({
'obj' : $('#add_note_form'),
'uri' : '/contact/add_note/'
});
}
});
});
This function when a new note is created calls a callback to validate the form fields first and then if successful calls a callback inside a seperate class to conduct the update. See the modal_update class below:
// Update modal
this.modal_update = function(data)
{//
// Declare a few variables for the data object we've received
obj = data.obj // The form element to serialize
uri = data.uri;
// Get the form ID from the data-target attribute
id = obj.attr('data-target');
// URL to send to
url = this.site_url + uri + id;
// The form object
this.post_data(obj.serialize(),url);
// Hide Modal
obj.closest('.modal').modal('hide');
// Refresh
this.refresh();
}
This then figures out the correct route to ajax and calls a ajax call back inside the same class:
// AJAX post
this.post_data = function(obj,uri)
{
$.ajax({
data: obj,
dataType: 'json',
type: 'post',
url: uri,
headers: { "cache-control": "no-cache" },
cache: false,
success: function (response) {
if (response.success == true)
{
$("#alert_success .msg").html(response.message);
$("#alert_success").fadeIn(200).delay(2000).fadeOut(200);
}
else
{
$("#alert_error .msg").html(response.error);
$("#alert_error").fadeIn(200).delay(2000).fadeOut(200);
console.log(response.error);
}
}
});
}
I am then running another class callback to "refresh" the data in all the elements on the page:
this.refresh = function()
{
// Refresh the ajax requests
this.get_contact_data();
this.get_notes();
this.get_contact_log();
this.get_contact_tasks();
}
This class re loads the functions which run on page load to get the inial data into the tables/fields on the page. See "get_notes" below:
// Get notes
this.get_notes = function()
{
// Get all notes and populate table
var log_uri = this.site_url + "/contact/get_notes/" + this.contact_id;
this.get_data(log_uri,function(data) {
notes = $("#contact_notes ul");
notes.empty("");
// Populate the contact fields, assuming there is a result to play with
if (data != false) {
//alert(JSON.stringify(data));
$("#notes-tab .count").html("(" + data.length + ")");
$.each( data, function( key, value ) {
notes.append("<li class='list-group-item' modal-id='editNoteModal' data-target='" + value.ID + "'><div class='row'><div class='col-lg-3'><i class='fa fa-sticky-note mr-3'></i>" + value.timestamp + "</div><div class='col-lg-7'>" + value.note + "</div><div class='col-lg-2'><a href='#' class='edit mr-3'><i class='fa fa-edit mr-1'></i>Edit</a><a href='#' class='delete'><i class='fa fa-times mr-1'></i>Remove</a></div></div></li>");
});
console.log('Notes loaded');
} else {
notes.append("<li>There are currently no notes for this contact</li>");
}
});
}
Now the problem:
For some reason this does not update consistently in real time. The data is updated fine on the server side but on the client side the update/refresh does not always update. I might add a note and get a correct update response but the refresh method seems to be receiving the old data and always be one note behind. So the next time I add a note, the one I added before then appears and so forth.
Another problem I am experiencing is the methods seem to stack on each event so if I add one note (or one of the other methods) I will see the console say "notes loaded" but on the second note it says "notes loaded" twice, then on the 3rd note added 3 times and so forth.
I am sure there must be something fatal flaw in the design of my code here but I am not experienced enough with javascript/jquery to notice what direction I am going wrong so I can fix it.
I thought that this was an issue with ajax caching and not refreshing the result so I have adjusted the ajax request as cache none and also to send no cache headers. I am running in wamp.
In your case, your refresh code will always run before your data got updated. Because ajax is asynchronous so the code behind and below ajax will always execute nearly the time your ajax running.
At the time you run your post_data function to call the API, the refresh function got run too. So it's done before your data got updated.
You should run refresh function inside ajax callback. For example:
this.post_data = function(obj,uri, callback)
{
$.ajax({
data: obj,
dataType: 'json',
type: 'post',
url: uri,
headers: { "cache-control": "no-cache" },
cache: false,
success: function (response) {
if (response.success == true)
{
$("#alert_success .msg").html(response.message);
$("#alert_success").fadeIn(200).delay(2000).fadeOut(200);
}
else
{
$("#alert_error .msg").html(response.error);
$("#alert_error").fadeIn(200).delay(2000).fadeOut(200);
console.log(response.error);
}
callback();
}
});
}
And in modal_update, you pass refresh function to post_data as a callback:
this.modal_update = function(data)
{//
// Declare a few variables for the data object we've received
obj = data.obj // The form element to serialize
uri = data.uri;
// Get the form ID from the data-target attribute
id = obj.attr('data-target');
// URL to send to
url = this.site_url + uri + id;
// The form object
this.post_data(obj.serialize(),url, this.refresh);
// Hide Modal
obj.closest('.modal').modal('hide');
}
You should read more about asynchronous ajax. You can use other tricky solution is setTimeout to run this.refresh but I do not recommend that because you not sure when the update is done.

JS AJAX typeahead result ordering / race condition

I'm encountering an issue with AJAX typeahead / live-update as-you-type views which are returning results out of order. Does anyone have any experience with methods of dealing with this?
The problem:
Type in a query, such as "search term".
Even with some debouncing, it's likely to fire off a few AJAX calls as you type, say "sea", and "search term".
The search result set for sea is larger than the one for search term, and so its AJAX request actually completes after the newer query.
The resulting problem: You type in search term, but the correct results blip across the screen for a second only to be replaced by the results for sea.
Bare-bones jQuery pseudocode:
$('body').on('keyup', '#searchBox', function(){
query = $("#searchBox").val();
$.ajax({
type: 'GET',
url: '/endpoint.php?query=' + query,
success: function (response) {
// Update view here
$("#view").html(response.text);
}
});
});
Angular pseudocode:
// HTML
<input ng-keyup = "search()" ng-model="searchBox" placeholder = "Search">
{{results}}
// JS
$scope.search = function(){
$http({
method: 'GET',
url: '/endpoint.php?query=' + $scope.searchBox
}).then(function successCallback(response) {
$scope.results = response.data;
}, function errorCallback(response) {
console.log(response);
});
}
Top stop the race condition you can keep a reference to the last AJAX request you made and then abort() it when a new key is pressed. It would also be worthwhile putting a delay before making the request so that you don't send a request for every key pressed, but for when typing ends. Try this:
var previousRequest, previousTimer;
$('body').on('keyup', '#searchBox', function() {
previousRequest && previousRequest.abort();
clearTimeout(previousTimer);
previousTimer = setTimeout(function() {
previousRequest = $.ajax({
type: 'GET',
url: '/endpoint.php',
data: {
query: $("#searchBox").val()
},
success: function (response) {
// Update view here
$("#view").html(response.text);
}
});
}, 200);
});
200ms between keystrokes is plenty, but you can shorten if you want to increase the responsiveness of the requests.

How to call ajax again when user click back button to go back last webpage?

Below is my code..
HTML Code
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="body">
<div class="dropdown_div">
<select id="q_type" class="dropdown" onchange="getSubject(this.value)">
<option>Question1</option>
<option>Question2</option>
</select>
</div>
<div class="dropdown_div">
<select id="q_subject" class="dropdown">
<option>Subject1</option>
</select>
</div>
</div>
JS Code
function getSubject(val){
$("option", $("#q_subject")).remove();
var option = "<option>Subject</option>";
$("#q_subject").append(option);
$.ajax({
url: "api.path",
type: 'POST',
dataType: 'json',
data: {id: id},
async: true,
cache: false,
success: function(response) {
alert("Hi");
$("option", $("#q_subject")).remove();
var option = "<option>Subject1</option>";
option += "<option value=1234>Subject2</option>";
$("#q_subject").append(option);
}
});
}
How do I use pushState into my code and let user can click back button to return last page and then still see the ajax data?
First of all, you should save data received from ajax request to browser local storage. Afterwards, in order to show ajax result when browser "back" button was fired, you should bind statements that you are calling in ajax.success() method to window onpopstate event. To omit code duplication, it`s better to use a declared function instead of anonymous one.
function success(response) {
alert("Hi");
$("option", $("#q_subject")).remove();
var option = "<option>Subject1</option>";
option += "<option value=1234>Subject2</option>";
$("#q_subject").append(option);
}
Save data to localstorage and call success function:
$.ajax({
url: "api.path",
type: 'POST',
dataType: 'json',
data: {id: id},
async: true,
cache: false,
success: function(response) {
localStorage.setItem("response", response);
success(response);
}
});
Call success() when "back" button was fired:
window.onpopstate = function (e) {
var res = localStorage.getItem('response');
success(res);
}
I would rather suggest you to use sessionStorage which expires when the browser window is closed :)
$.ajax({
url: "api.path",
type: 'POST',
dataType: 'json',
data: {id: id},
async: true,
cache: false,
success: function(response) {
sessionStorage.setItem("DataSaved", response);
success(response);
}
});
And then
window.onpopstate = function (e) {
var res = sessionStorage.getItem('DataSaved');
success(res);
}
You can solve this using the local Storage or Session storage. You will also need to have a onload function callback, to check if there are any previous values that you stored in the local/session storage, if yes, then show that data in the select box.
I noticed this Back() issue when using Ajax to navigate an MVC-5 application from within a JavaScript generated diagram. All clicks in the diagram are handled by Ajax.
Above solutions do not replace the complete body, in the repaired cases a Back() would restore just the edit fields. In my case, I don't need that. I need to replace the entire page from the AJAX and also enable the Back button to return to my original diagram context.
I tried above solution to replace body, and I have to note, it would only trigger the window.pop event after
history.pushState({}, '')
But when the event triggered and it uses Ajax to fill the body, my Javascript would not properly re-initialize the diagram page.
I decided to use another pattern, to circumvent the the window.pop event and avoid the back-issue. Below code will not return into the Ajax code context, but instead simply replace current page, processing the Ajax return information from the server (=Controller) as a redirect link, like
var url = "/ProcessDiagram/MenuClick?command=" + idmenuparent+"_"+citem; // my Ajax
$.get(url,
function (data) {
window.location = data; // Server returns a link, go for it !
return true; // Just return true after going to the link
});
.. this will preserve the Back() context, because the browser will take care of things.
Controller side composes the redirect link, like
public ActionResult MenuClick(string command)
{
List<string> sl = command.Split(new char[] {'_'}).ToList();
var prId = int.Parse(sl[0].Substring(3));
if (sl[1] == "PU")
return Content("/ProductionUnitTypes/Details/" + UnitContextId(prId) );
if (sl[1] == "IR")
return Content("/ItemRoles/Details/" + RoleContextId(prId) );
// etcetera
}
I solved it by including the below code just before the $.get() function
$.ajaxSetup({cache: false});
It works! Try it :)

MySQL / AJAX / jQuery Autocomplete

I am attempting to make a search bar on the top of my website which will search through a table with relatively low stringency. Basically, the user types a key word corresponding to one of the three columns below and they will send an AJAX request and a link to the corresponding page will pop up in my div.
For some reason, no matter what I try, I can't get the "5gooper" entry to show up. When I search "a" or "aa", the other two show up but no combination of letters will return the 5gooper entry. Here is my code:
PHP
$searchquery = $_POST['searchquery'];
$searchquery2 = "%$searchquery%";
$query = "SELECT * FROM data WHERE
name LIKE '$searchquery2'
OR author LIKE '$searchquery2'
OR project LIKE '$searchquery2'
OR protocol LIKE '$searchquery2'
ORDER BY DATE DESC LIMIT 20";
AJAX
$('#sbar').keyup(function(){
var query = $(this).val();
$.ajax
({
url: "autocomplete.php",
data: {searchquery: query},
type: 'POST',
success: function(data){
$('#acd').html(data); //acd stands for auto complete div
}
});
});
Any idea as to why the search isn't entirely working? Even with flanking '%'s, it won't find the gooper entry, no matter what I type.
Thanks
This is how I would do it:
$('#search-input input[type=text]').on('keyup', function(){
var input = $('#search-input input[type=text]').val();
var $name = $.trim(input);
if($name.length > 3 && $name.length < 25) {
delay(function() {
var url = encodeURI("search/" + $name);
$.ajax({
url: url,
type: "get",
dataType: 'json',
success: function(data) {
console.log(data);
}
});
}, 500);
}
});
and my delay function
var delay = (function() {
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
This is for GET and can easily be changed to POST. It's equipped with spam protection by executing the ajax call every 500 ms and the character range can be adjusted to work after x characters but no more than x characters.

Make javascript execute linearly

I am building a game. When a piece is moved ('.draggable'), I need make the first ajax call (to move_script.php) which will validate the move and then save it in a database. After that is done, I want to run the second ajax call (to past_moves.php) and query the database and write out the new move.
My problem, is that sometimes the second ajax calls pulls out the results BEFORE the current move is there. I've used microtime(); in the php code to see what is happening, and every time that the most recent result isn't pulled, the second ajax script is completing first. (Most of the time the most recent result comes out, but every so often it won't, it seems to be randomly, every 5 - 15 times or so).
How can I ensure that the second ajax call won't run until the first one is done?
Thank you
code:
<script>
$(function() {
$( ".draggable" ).draggable({
stop: function(event, ui) {
var start = $(this).attr('id')
//ajax call number 1
$.ajax({
type: "POST",
url: "../move_script.php",
data: { start: start }
}).done(function( msg ) {
//alert( "Data Saved: " + msg );
$("#chessboard").html(msg);
});
//ajax call number 2
$.ajax({
type: "POST",
url: "../past_moves.php",
data: {}
}).done(function( moves ) {
$("#past_moves").html( moves );
});
}
});
});
</script>
Nest the ajax calls
//ajax call number 1
$.ajax({
type: "POST",
url: "../move_script.php",
data: { start: start }
}).done(function( msg ) {
//alert( "Data Saved: " + msg );
$("#chessboard").html(msg);
//ajax call number 2
$.ajax({
type: "POST",
url: "../past_moves.php",
data: {}
}).done(function( moves ) {
$("#past_moves").html( moves );
});
});
});
This nesting will force the second call to wait until the first has completed.
Put the second ajax call in the callback of the first ajax call to make sure it only executes after it is complete. Otherwise, since ajax is asynchronous, it starts the request and then keeps going (in this case, starting the second ajax). I'm not familiar with the "done" method - maybe you can put it in that function that you already have. Otherwise, this is the syntax I'm familiar with:
$.ajax({
type: "POST",
url: "../move_script.php",
data: { start: start },
success: function(data){
//data is whatever the ajax returns. is it msg in this case?
//put second ajax call here
},
error: function(jqXHR, textStatus, errorThrown){
//might not need here, but this is example of callback which will be executed instead of success if server returns an error
},
complete: function(jqXHR, textStatus){
//another useful callback to know about - will execute after success or error
}
})
https://api.jquery.com/jQuery.ajax/

Categories

Resources