I am new to AJAX. A want to make a call to a django view to update item quantity and change number on the page.
I have a template
...
{% for item in items %}
<div id="quantity">{{ item.quantity }}</div>
<button onclick="updateQuantity('parts/quotes/ajax/increase-item/')">+</button>
...
<script>
function docReady(fn) {
if (document.readyState === "complete" || document.readyState === "interactive") {
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
};
};
docReady(function() {
function updateQuantity(url) {
fetch(url, {
method: "GET",
headers: {
"X-Requested-With": "XMLHttpRequest",
}
})
.then(response => response.json())
.then(data => {
document.getElementById("quantity").innerHTML = data;
});
}
});
</script>
And a view:
def increase_quote_item_ajax(request):
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
if is_ajax:
if request.method == 'GET':
item = QuoteItem.objects.get(id=pk)
item.quantity += 1
item.save(update_fields=['quantity'])
return JsonResponse({'data': item.quantity})
return JsonResponse({'status': 'Invalid request'}, status=400)
else:
return HttpResponseBadRequest('Invalid request')
But when I click on + button I get
Uncaught ReferenceError: updateQuantity is not defined
Why isn’t my function registering?
UPDATE:
Per David’s advice:
{% for item in items %}
<span>{{ item.quantity }}</span><button class="increase-button" data-foo=""{{ item.get_increase_url_ajax }}"">+</button>
...
<script>
const buttons = document.getElementsByClassName('quantity-button');
for (const button of buttons) {
button.addEventListener('click', function (e) {
fetch(e.target.dataset.foo, {
method: "GET",
headers: {
"X-Requested-With": "XMLHttpRequest",
},
})
.then(response => response.json())
.then(data => {
button.previousSibling.innerHTML = data['quantity'];
});
});
}
</script>
What’s a proper way to pass the id
in the fetch request to the django view? I don’t think I am supposed to do that in headers.
2
Answers
updateQuantity
isn’t defined in global (window) scope, only in a specific function. So the<button>
can’t invoke it directly like that.One approach would be to move the
updateQuantity
function out of that anonymous function and into global scope:Since all this does is define a function then it shouldn’t need to wait for the
DOMContentLoaded
event. If you do want/need to wait for that event, you can manually assign the function to thewindow
object:Alternatively, instead of calling the function directly in the
<button>
markup at all, assign a click handler where you define the function. For example, if you give the<button>
anid
:Then when defining the function you can use that
id
(or any selector you like, this is just for example) to attach the click handler to the button:The error you’re encountering, "Uncaught ReferenceError: updateQuantity is not defined," occurs because the updateQuantity function is defined within the docReady function’s scope, and it’s not accessible in the global scope where the onclick attribute of the button is looking for it. To make the updateQuantity function accessible to the HTML button, you should define it in the global scope.
You can modify your code like this to define the updateQuantity function in the global scope:
In this updated code, I’ve passed the item.id to the updateQuantity function as a parameter. Also, I’ve modified the elementId in the document.getElementById call to be elementId, and I’ve added the item.id to the element’s id attribute to make each div element unique.
This way, each button will call the updateQuantity function with a unique URL and elementId, ensuring that the correct quantity is updated on the page.