Handling async ajax calls within django session - javascript

I have a Django application, where the template contains for loop in javascript, which iterates all of the check-boxes in some table. For each check box, I send an ajax request to a view function, where I want to save the id of the check box in a list or remove the id from the list (depends on the check state). I need the list to be a part of the request.session dictionary.
The results show me that the ajax calls are asynchronous and that makes my list to be wrongly updated, and inconsistent.
Are there some thread safe data structures which I can store as part of the session, and ensure sync list updating?
JavaScript and Ajax:
function checkAll(source, type) {
checkboxes = document.getElementsByName(type);
for(var i=0, n=checkboxes.length;i<n;i++) {
if (checkboxes[i].checked != source.checked) {
checkboxes[i].checked = source.checked;
select_row(checkboxes[i], source.checked);
}
}
}
function select_row(row_selector, is_checked) {
is_box_checked = typeof is_checked !== 'undefined' ? is_checked : row_selector.checked;
request = {
url: "{% url 'set_check_box' %}",
type: "POST",
contentType: "application/x-www-form-urlencoded",
data: {
csrfmiddlewaretoken: "{{ csrf_token }}",
checked: is_box_checked,
check_box_id: row_selector.id,
type: row_selector.name
},
error: function(response, status, error_msg) {
console.log(error_msg);
}
};
$.ajax(request);
}
View function:
def set_check_box(request):
request.session.modified = True
check_box_list = list(request.session['connects_check_boxes_id_list'])
check_box_id = request.POST["check_box_id"]
is_checked = json.loads(request.POST['checked'])
if is_checked:
check_box_list.append(check_box_id)
else:
check_box_list.remove(check_box_id)
request.session['connects_check_boxes_id_list'] = list(check_box_list)
return HttpResponse("")

All I had to do is set async option to false as part of the request parameters.
function select_row(row_selector, is_checked) {
is_box_checked = typeof is_checked !== 'undefined' ? is_checked : row_selector.checked;
request = {
url: "{% url 'set_check_box' %}",
type: "POST",
contentType: "application/x-www-form-urlencoded",
async: false,
data: {
csrfmiddlewaretoken: "{{ csrf_token }}",
checked: is_box_checked,
check_box_id: row_selector.id,
type: row_selector.name
},
error: function(response, status, error_msg) {
console.log(error_msg);
}
};
$.ajax(request);
}

Related

Call Laravel Model Function from Blade Button OnClick Javascript Function and Stay On the Page

Goal:
A user will have a list of games in a table with text boxes for each team's score. I want the user to be able to change the score of a single game, click Save (Model function updates the record), and continue saving more games while never leaving the page.
How:
After a Laravel Blade view has been rendered, I want to execute a Model function from a Javascript function on-button-click, but stay on the same page.
admin.blade.php (Javascript section in Head tag)
/* Save game from inline list on Admin page */
function inlineSaveAdmin(gameId) {
var homeScoreTxt = document.getElementById("homeScoreTxtBox");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox");
var awayScore = awayScoreTxt.value;
{{ App\Models\Game::inlineSave(gameId, homeScore, awayScore) }}
}
admin.blade.php (body of view)
<button type="button" onclick="inlineSaveAdmin({{ $game->id }});" class="btn btn-outline-success">Save</button>
So far, the Model function only executes when the page loads, not when I click the button. That is the main problem I wish to solve. Thanks for any help!
(and yes, I believe that I will need to create identical Javascript functions for each gameId that exists to be able to reference the correct homeScoreTxtBox{{ game->id }} since I don't think I could otherwise dynamically pull the text box IDs based on the Javascript function's input parameter)
1.make an ajax function on that blade file
2.call that ajax on click pass the id and updated data
3.define a route for that ajax function in web.php and
4.make a controller function on that route.
Code:
$(document).ready(function() {
$("#button").on('click', function() {
**//get id and score**
var homeScoreTxt = document.getElementById("homeScoreTxtBox");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox");
var awayScore = awayScoreTxt.value;
var game_id = gameId;
$.ajax({
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: '{{ route('update') }}',
//all the data you need to pass to controller function
data: {
'id': gameId,
'homescore': homeScore,
'awayscore' : awayScore
},
// dataType: 'json',
success: function(data) {
//data returned from php
// update the values
if (data) {
homeScoreTxt.value=data.homeScore,
awayScoreTxt.value=data.homeScore
}
},
fail: function() {
alert('NO');
}
});
});
});
web.php
Route::post('update', 'UpdateController#update')->name('update');
Update the values in the controller function by simple model queries.
Send updated data like this:
$response = [
'homeScore' => $homeScore,
'awayScore' => $awayScore
];
return response()->json($response);
I have followed Daniyal Ishaq's answer, and I think I'm getting closer, but I'm getting an error from the Ajax call.
Error:
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
(jquery-3.5.1.js:10099) xhr.send( options.hasContent && options.data || null );
Per Google debugger, it appears to be after/inside this call:
(jquery-3.5.1.js:9682) transport.send( requestHeaders, done );
I did some debugging, and a "status" variable is getting set to 500. Then, "isSuccess" is set to False when it gets to this line:
(jquery-3.5.1.js:9723) isSuccess = status >= 200 && status < 300 || status === 304;
That line that sets isSuccess is inside the following function, but I cannot seem to find where it's getting called from to trace where status is getting set exactly.
(jquery-3.5.1.js:9696) function done( status, nativeStatusText, responses, headers ) {
The last line I can find before the error appears is 5233:
(jquery-3.5.1.js:5233) jQuery.event.dispatch.apply( elem, arguments ) : undefined;
Shortly before that line, it is here, where event.rnamespace = undefined, and handleObj.namespace = "" (I don't know if this is relevant):
(jquery-3.5.1.js:5422) if ( !event.rnamespace || handleObj.namespace === false ||
Shortly after that, "ret" is still undefined after this line: (again, I don't know what this does, but it seems important?)
ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
handleObj.handler ).apply( matched.elem, args );
Then on 5446, it returns event.result, which is undefined.
return event.result;
That is where my debugging skills hit a dead end with jQuery. So now I ask for more help.
Ajax function in blade:
$(document).ready(function() {
#foreach($games as $game)
$("#SaveBtn{{ $game->id }}").on('click', function() {
var gameId = "{{ $game->id }}";
var saveBtn = document.getElementById("SaveBtn{{ $game->id }}");
var homeScoreTxt = document.getElementById("homeScoreTxtBox{{ $game->id }}");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox{{ $game->id }}");
var awayScore = awayScoreTxt.value;
$.ajax({
url: "{{ route('inlineSave') }}",
type: "POST",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
//all the data you need to pass to controller function
data: {
'gameId' : {{ $game-> id }},
'homeScore': homeScore,
'awayScore' : awayScore
},
dataType: "json",
traditional: true,
success: function(data) {
//data returned from php
// update the values
if (data) {
homeScoreTxt.value = data.homeScore;
awayScoreTxt.value = data.awayScore;
saveBtn.innerText = 'Resave';
alert('Success!');
}
},
error: function() {
alert('An error has occurred!');
}
});
});
#endforeach
});
Resulting HTML for Ajax function:
$(document).ready(function() {
$("#SaveBtn11870").on('click', function() {
var gameId = "11870";
var saveBtn = document.getElementById("SaveBtn11870");
var homeScoreTxt = document.getElementById("homeScoreTxtBox11870");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox11870");
var awayScore = awayScoreTxt.value;
$.ajax({
url: "http://mbcathletics/admin",
type: "POST",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
//all the data you need to pass to controller function
data: {
'gameId' : 11870,
'homeScore': homeScore,
'awayScore' : awayScore
},
dataType: "json",
traditional: true,
success: function(data) {
//data returned from php
// update the values
if (data) {
homeScoreTxt.value = data.homeScore;
awayScoreTxt.value = data.awayScore;
saveBtn.innerText = 'Resave';
alert('Success!');
}
},
error: function() {
alert('An error has occurred!');
}
});
});
... many more of the same function for different button IDs ...
});
Button in blade: (calls its respective function successfully)
<button id="SaveBtn{{ $game->id }}" type="button" class="btn btn-outline-success">Save</button>
Route in web.php: (remember, I do not want to leave the page, I just want it to execute the Controller function... I don't know what to put in the first parameter - the URL)
Route::post('/admin', [App\Http\Controllers\HomeController::class, 'inlineSave'])->name('inlineSave');
Controller function: (it doesn't really do anything right now, I'm just trying to test connectivity before I do the heavy lifting)
public static function inlineSave()
{
$game = Game::find($gameId);
$score = $game->home_score;
$game->home_score = $score;
$response = [
'homeScore' => $homeScore,
'awayScore' => $awayScore
];
return response()->json($response);
}
Thank you! I am sorry for the detail, but it's the only I know how to help.

How can I display alert only once in javascript?

My case like this :
if(window.location.pathname == '/shop/payment/checkout' || window.location.pathname == '/shop/detail' || window.location.pathname == '/shop') {
alert('Your data has been removed')
localStorage.removeItem("cartCache")
var _token = $('input[name="_token"]').val();
$.ajax({
type: 'POST',
url: baseUrl+'/shop/delete-cache',
data: {_token: _token},
success: function(response){
if(response.success==1)
window.location = "/shop";
},
error: function(request, status, error) {
console.log('error')
}
});
}
If the url accessed meets the condition of if then it will delete session by ajax and redirect to the url /shop
My problem is if redirect to url /shop, it will check again and display alert message again. So on and on
I want if the alert message appears and reload to the url /shop, it does not check anymore and displays the alert message
How can I do it?
EDIT:
After the answer given, I wrapped my code like this:
if (localStorage.getItem("cartCache") !== null) {
...
}
else {
alert('Your data has been removed')
localStorage.removeItem("cartCache")
var _token = $('input[name="_token"]').val();
$.ajax({
type: 'POST',
url: baseUrl+'/shop/delete-cache',
data: {_token: _token},
success: function(response){
if(response.success==1)
window.location = "/shop";
},
error: function(request, status, error) {
console.log('error')
}
});
}
}
It does not work as intended.
Before removing, you could first check if the local storage data is still there. Put this before the alert:
if (localStorage.getItem("cartCache") === null) return;
... assuming this code is within a function. But you get the idea. Or you can combine it with the if you already have (a bit improved):
if(['/shop/payment/checkout', '/shop/detail', '/shop'].includes(window.location.pathname)
&& localStorage.getItem("cartCache") !== null) {
// ...etc.

jQuery .ajax() - add query parameters to POST request?

To add query parameters to a url using jQuery AJAX, you do this:
$.ajax({
url: 'www.some.url',
method: 'GET',
data: {
param1: 'val1'
}
)}
Which results in a url like www.some.url?param1=val1
How do I do the same when the method is POST? When that is the case, data no longer gets appended as query parameters - it instead makes up the body of the request.
I know that I could manually append the params to the url manually before the ajax request, but I just have this nagging feeling that I'm missing some obvious way to do this that is shorter than the ~5 lines I'll need to execute before the ajax call.
jQuery.param() allows you to serialize the properties of an object as a query string, which you could append to the URL yourself:
$.ajax({
url: 'http://www.example.com?' + $.param({ paramInQuery: 1 }),
method: 'POST',
data: {
paramInBody: 2
}
});
Thank you #Ates Goral for the jQuery.ajaxPrefilter() tip. My problem was I could not change the url because it was bound to kendoGrid and the backend web API didn't support kendoGrid's server paging options (i.e. page, pageSize, skip and take). Furthermore, the backend paging options had to be query parameters of a different name. So had to put a property in data to trigger the prefiltering.
var grid = $('#grid').kendoGrid({
// options here...
dataSource: {
transport: {
read: {
url: url,
contentType: 'application/json',
dataType: 'json',
type: httpRequestType,
beforeSend: authentication.beforeSend,
data: function(data) {
// added preFilterMe property
if (httpRequestType === 'POST') {
return {
preFilterMe: true,
parameters: parameters,
page: data.page,
itemsPerPage: data.pageSize,
};
}
return {
page: data.page,
itemsPerPage: data.pageSize,
};
},
},
},
},
});
As you can see, the transport.read options are the same options for jQuery.ajax(). And in the prefiltering bit:
$.ajaxPrefilter(function(options, originalOptions, xhr) {
// only mess with POST request as GET requests automatically
// put the data as query parameters
if (originalOptions.type === 'POST' && originalOptions.data.preFilterMe) {
options.url = options.url + '?page=' + originalOptions.data.page
+ '&itemsPerPage=' + originalOptions.data.itemsPerPage;
if (originalOptions.data.parameters.length > 0) {
options.data = JSON.stringify(originalOptions.data.parameters);
}
}
});

Django Ajax returns whole html page

I'm trying to create live search filter,with ajax
$(function() {
$('#search-item').keyup(function() {
$.ajax({
type: "GET",
url: "/toysprices/",
data: {
'query' : $('#search-toy').val(),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: searchSuccess,
dataType: 'html'
});
});
});
function searchSuccess(data, textStatus, jqXHR)
{
console.log(data);
}
and my views.py
f request.method == "GET":
search_text = request.GET['query']
if search_text:
search_text = request.GET['query']
statuss = Status.objects.filter(status__contains = search_text)
else:
statuss = Status.objects.all()
return render(request, 'ajax_search.html', {'statuss':statuss})
it works correctly, but it returns whole html page, how can i make to get only part which I want to render in my template.
Returning the result with JSON will solve your problem.
For Example,
# Django view
def search(request):
if request.method == "GET":
return_array = []
search_text = request.GET.get('query') # Always put request.GET.get('param') instead of request.GET['param']
if search_text:
search_text = request.GET.get('query')
statuss = Status.objects.filter(status__icontains = search_text)
else:
statuss = Status.objects.all()
for i in statuss:
return_sub_array = {}
return_sub_array['status_name'] = i.status_name
return_array.append(return_sub_array)
return HttpResponse(json.dumps(return_array))
# Jquery function
$('#search-item').keyup(function() {
$.ajax({
type: "GET",
url: "/toysprices/",
dataType: 'JSON',
data: {
'query' : $('#search-toy').val(),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: function(data){
if(data.length > 0 )
{
console.log(data);
for (var i = 0; i < data.length ; i++) {
var obj = data[i]['status_name'];
console.log(obj)
// further logic goes here
}
}
else {
console.log("No result found");
}
},
error:function(data){
console.log('error')
console.log(data)
}
});
});
In most cases it is the url which you are using to call, assuming you have the following path in the url.py
path('Import/', views.Import, name='import'),#.....1
path('getMetaData/', views.metaData, name='metadata'),#....2
and your url: http://127.0.0.1:8000/folder/Import/ is using the first path which is showing the page, if you wish to get data from ajax from the metaData function in views.py, if you use path 2 above it will give you html, so your path should be as follows:
path('Import/getMetaData/', views.metaData, name='metadata'),#....3
You are rendering html and returning it in your view. Here's nothing to expect from this view other than html. In order to return JSON object as a response, your view should return response like this:
return JsonResponse({'statuss':statuss})

How do I get the right $.ajax data type

could you please help with this. I have the following javascript:
$('form').click(function (e)
{
if (e.target.getAttribute('id') === 'SubmitAddLevel')
{
var parent = $('#' + e.target.getAttribute('attr')),
var Data = [];
parent.find('.input').children().each(function (i, e)
{
Data.push(e.getAttribute('id') + ":" + e.value);
console.log(Data);
});
$.ajax({
type: "POST",
url: 'AjaxControls.aspx/CreateUserLevel',
//data: Data, //.join(','),
dataType: "text",
contentType: "application/json; charset=utf-8",
//error: function (er) { alert(er); },
success: function (response)
{
if (response.d === "true")
{
$("#ErrorDivAddLevel").html('Level created successfully!').fadeIn('slow');
}
else
{
$("#SuccessDivAddLevel").html('Level creation failed!').fadeIn('slow');
}
},
});
}
The result of 'Data' I got on the console is :["LevelNameAddLevel:Admin", "PriviledgeIDAddLevels:|1|2|3|4|5|6|7|"]. How do I convert this to what ajax will pass to my web menthod?
Here is the web method
<WebMethod(EnableSession:=True)>
Public Shared Function CreateUserLevel(userLevel As String, userPriviledges As String) As String
return "true"
end function
I think your Data should look something more like this:
[{"LevelNameAddLevel":"Admin"}, {"PriviledgeIDAddLevels":"|1|2|3|4|5|6|7|"}]
So you have key / value pairs inside of an array. In the request, you should then be able to fetch the data via the keys in the request.
But I'm not quite sure what this is supposed to mean : "|1|2|3|4|5|6|7|"

Categories

Resources