I have a Django project where I am trying to get AJAX working. I can't seem to send the JSON response to the HTML page. When I check the chrome console the JSON data is not returned only parses the HTML.
This is my Views.py where I have the cart logic defined:
def cart_home(request):
cart_obj, new_obj = Cart.objects.new_or_get(request)
return render(request, "carts/carts.html", {"cart": cart_obj})
def cart_update(request):
print("Hello")
product_id = request.POST.get('product_id')
if product_id is not None:
try:
product_obj = Product.objects.get(id=product_id)
except Product.DoesNotExist:
print("Product is gone")
return redirect("cart:home")
cart_obj, new_obj = Cart.objects.new_or_get(request)
if product_obj in cart_obj.products.all():
cart_obj.products.remove(product_obj)
added = False
else:
cart_obj.products.add(product_obj)
added = True
request.session['cart_items'] = cart_obj.products.count()
if request.is_ajax():
print("Ajax request")
json_data = {
"added": added,
"removed": not added,
}
return JsonResponse(json_data)
return redirect("cart:home")
This is javascript for the Ajax:
<script>
$(document).ready(function(){
var productForm = $(".form-product-ajax")
productForm.submit(function(event){
event.preventDefault();
console.log("Form is not sending")
var thisForm = $(this)
var actionEndpoint; thisForm.attr("action");
var httpMethod; thisForm.attr("method");
var formData; thisForm.serialize();
$.ajax({
url: actionEndpoint,
method: httpMethod,
data: formData,
success: function(data){
console.log("success")
console.log(data)
console.log(data.added)
console.log(data.removed)
console.log("Added", data.added)
console.log("Removed", data.removed)
var submitSpan = thisForm.find(".submit-span")
if (data.added){
submitSpan.html("<button>Remove</button>")
} else {
submitspan.html("<button>Add to Basket</button>")
}
},
error: function(errorData){
console.log("error")
console.log(errorData)
}
})
})
})
</script>
Added screenshot of Chrome Inspector (Network Tab)
Related
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.
I have a Flask web app that receives AJAX statuses/requests via request.args.get however one of my requests aren't being read by my Flask app. The other status status works fine, but doorStatus does not.
The data is successfully sent by AJAX since I can see the reqeust when I go to dev tools > network in my browser. Am I missing something?
from flask import Flask, request, jsonify
import serial
import time
app = Flask(__name__)
ser = serial.Serial('/dev/ttyACM0', 9600)
# {{url}}/led?status=on
#app.route('/', methods=['GET'])
def led():
status = request.args.get('status')
if status == "on":
exteriorOn = 'H'
exteriorOnEncode = exteriorOn.encode()
ser.write(exteriorOnEncode)
return jsonify({"message": "Led successfully turned on"})
elif status == "off":
exteriorOff = 'C'
exteriorOffEncode = exteriorOff.encode()
ser.write(exteriorOffEncode)
return jsonify({"message": "Led successfully turned off"})
else:
return jsonify({"message": "Not a valid status"})
doorStatus = request.args.get('doorStatus')
if doorStatus == "open":
doorOpen = 'L'
doorOpenEncode = doorOpen.encode()
ser.write(doorOpenEncode)
return jsonify({"message": "Door successfully opened"})
elif doorStatus == "closed":
doorClosed = 'N'
doorClosedEncode = doorClosed.encode()
ser.write(doorClosedEncode)
return jsonify({"message" : "Door successfulled closed"})
else:
return jsonify({"message" : "Not a valid status"})
JS
$(document).ready(function() {
$('#openDoor').on('click', function(e){
let doorStatus;
if($(this).text() == 'OPEN') {
$(this).text('CLOSE')
doorStatus = 'open';
} else {
$(this).text('OPEN');
doorStatus = 'closed';
}
$.ajax({
url: '/led',
method: 'GET',
data: {doorStatus},
success: function(result) {
console.log(result);
}
});
e.preventDefault();
});
$('#exteriorBtn').on('click', function(e){
let status;
if($(this).text() == 'Exterior') {
$(this).text('Turn Off')
status = 'on';
} else {
$(this).text('Exterior');
status = 'off';
}
$.ajax({
url: '/led',
method: 'GET',
data:{status},
success: function(result) {
console.log(result);
}
});
e.preventDefault();
});
});
When you GET or POST data, it needs to have a name and value pair.
GET Example:
http://www.nowhere.place/webpage.html?status=open
You can see the name status and it's value open.
See more: https://api.jquery.com/jquery.ajax/
data
Type: PlainObject or String or Array
Data to be sent to the server. If the HTTP method is one that cannot have an entity body, such as GET, the data is appended to the URL
Plain Object Example:
data: { status: "OPEN" }
String Example:
data: encodeURIComponent("status=OPEN")
Array Example:
data: [ "OPEN" ]
So for your code, I would advise:
$('#openDoor').on('click', function(e){
e.preventDefault();
var door = { status: "" };
if($(this).text() == 'OPEN') {
$(this).text('CLOSE');
door.status = 'open';
} else {
$(this).text('OPEN');
door.status = 'closed';
}
$.ajax({
url: '/led',
method: 'GET',
data: door,
success: function(result) {
console.log(result);
}
});
});
I have a form, which create new record in db. It works because of ajax. I have argument 'id', which is created automatically, when new record is being created (after fill form). I want to use this id, because I need to redirect users to page /127.0.0.1:8000/<id>, when they fill form or maybe simply print this link in page. How can I do this?
views.py
def add_new(request):
""" Function which upload new file to UploadModel. """
form_upload = UploadForm(request.POST, request.FILES, prefix='upload_form')
if form_upload.is_valid() and request.is_ajax():
new_file = form_upload.save(commit=False)
if request.user.is_authenticated:
new_file.author = request.user
new_file.created_date = date.today()
new_file.is_worked = True
if new_file.ended_date <= date.today():
new_file.is_worked = False
new_file.delete()
else:
new_file.is_worked = True
new_file.save()
return redirect('index')
form_upload = UploadForm()
return render(request, 'sharing/index.html', {'form_upload': form_upload})
js file
function upload(event) {
event.preventDefault();
var data = new FormData($('form').get(0));
$.ajax({
url: $(this).attr('data-url'),
type: $(this).attr('method'),
data: data,
cache: false,
processData: false,
contentType: false,
success: handleSuccess(),
});
return false;
}
function handleSuccess(){
$("form")[0].reset();
alert('Success uploading!');
window.location.href = '/';
}
$(function() {
$('form').submit(upload);
});
import json
from django.http import HttpResponse
def add_new(request):
""" Function which upload new file to UploadModel. """
form_upload = UploadForm(request.POST, request.FILES, prefix='upload_form')
if form_upload.is_valid() and request.is_ajax():
new_file = form_upload.save(commit=False)
if request.user.is_authenticated:
new_file.author = request.user
new_file.created_date = date.today()
new_file.is_worked = True
if new_file.ended_date <= date.today():
new_file.is_worked = False
new_file.delete()
else:
new_file.is_worked = True
new_file.save()
response_data = {}
response_data['form_upload'] = UploadForm()
return HttResponse(json.dumps(response_data),content_type="application/json")
I am trying to submit a form via ajax like this:
$(document).ready(function () {
$("#form").submit(function (e) {
e.preventDefault();
var token = $('input[name="__RequestVerificationToken"]', this).val();
var form_data = $(this).serialize();
$.ajax({
url: "#Url.Action("SaveRole", #ViewContext.RouteData.Values["controller"].ToString())",
method: "POST",
data: form_data,
contentType: "application/json",
success: function (result) {
console.log(result);
},
error: function (error) {
console.log(error);
}
});
return false;
console.log(form_data);
});
});
This contacts this controller:
[HttpPost]
[ValidateAjax]
[ValidateAntiForgeryToken]
public ActionResult SaveRole(SaveRolesDetailsViewModel Input)
{
//var role = rolesData.GetByName(Input);
var result = this.Json(new
{
Output = Input
}, JsonRequestBehavior.DenyGet);
return result;
}
Right now, I'm getting errors that my RequestVerificationToken field is not being submitted but I'm not sure how I can combine it with my form data. By default, when I serialize my form data, it already sends this token but for some reason my controller still fails it.
Also, how do I use the modelstates to show my form validations? Right now they are returned as json objects.
EDIT:
AjaxValidate Attribute:
public class ValidateAjax : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
return;
var modelState = filterContext.Controller.ViewData.ModelState;
if (!modelState.IsValid)
{
var errorModel =
from x in modelState.Keys
where modelState[x].Errors.Count > 0
select new
{
key = x,
errors = modelState[x].Errors.
Select(y => y.ErrorMessage).
ToArray()
};
filterContext.Result = new JsonResult()
{
Data = errorModel
};
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
}
}
When I submit an empty form, this is what is returned:
0:{key: "RoleName", errors: ["The Role Name field is required."]}
When you use the jQuery .serialize() method, it generates query string format (i.e. ..&name=value&..., which needs to be sent using the default contentType - i.e 'application/x-www-form-urlencoded; charset=UTF-8'.
Remove the contentType: "application/json", from the ajax options.
In addition, your var token = $('input[name="__RequestVerificationToken"]', this).val(); line of code is not necessary - the token is included when you use .serialize()
$("#form").submit(function (e) {
e.preventDefault();
// Add the following if you have enabled client side validation
if (!$(this).valid()) {
return;
}
var form_data = $(this).serialize();
$.ajax({
url: "#Url.Action("SaveRole")",
method: "POST",
data: form_data,
success: function (result) {
... // see notes below
},
error: function (error) {
console.log(error);
}
});
// return false; not necessary since you have used e.preventDefault()
console.log(form_data);
});
To return ModelState errors, remove your ValidateAjaxAttribute - returning a BadRequest is not appropriate (which is intended to indicate that the server could not understand the request due to invalid syntax).
Instead modify the POST method to return a JsonResult that includes the errors (note there is no need to return the model)
public ActionResult SaveRole(SaveRolesDetailsViewModel Input)
{
if (ModelState.IsValid)
{
return Json(new { success = true });
}
else
{
var errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0).Select(k => new { propertyName = k, errorMessage = ModelState[k].Errors[0].ErrorMessage });
return Json(new { success = false, errors = errors });
}
}
Then in the success callback, if there are errors, loop through them and find the corresponding <span> element generated by your #Html.ValidationMessageFor() and update its contents and class names
success: function (result) {
if (result.success) {
return;
}
$.each(result.errors, function(index, item) {
// Get message placeholder
var element = $('[data-valmsg-for="' + item.propertyName + '"]');
// Update message
element.append($('<span></span>').text(item.errorMessage));
// Update class names
element.removeClass('field-validation-valid').addClass('field-validation-error');
$('#' + item.propertyName).removeClass('valid').addClass('input-validation-error');
});
},
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})