skip to Main Content

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


  1. 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:

    <script>
      function updateQuantity(url) {
        //...
      }
    </script>
    

    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 the window object:

    <script>
      docReady(function() {
        window.updateQuantity = function (url) {
          //...
        };
      });
    </script>
    

    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> an id:

    <button id="update-button">+</button>
    

    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:

    <script>
      docReady(function() {
        const url = 'parts/quotes/ajax/increase-item/';
    
        function updateQuantity() {
          //...
        }
    
        document.querySelector('#update-button').addEventListener('click', updateQuantity);
      });
    </script>
    
    Login or Signup to reply.
  2. 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:

    {% for item in items %}
      <div id="quantity{{ item.id }}">{{ item.quantity }}</div>
      <button onclick="updateQuantity('parts/quotes/ajax/increase-item/{{ item.id }}')">+</button>
    ...
    <script>
      function updateQuantity(url, elementId) {
        fetch(url, {
          method: "GET",
          headers: {
            "X-Requested-With": "XMLHttpRequest",
          }
        })
        .then(response => response.json())
        .then(data => {
          document.getElementById(elementId).innerHTML = data.data;
        });
      }
    </script>
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search