Search code examples
phpjqueryajax

Illegal Invocation error with FormData when using shorthand post in AJAX Request


I'm encountering the error "Illegal invocation" when attempting to use the shorthand syntax for sending form data with a file upload in an AJAX request using jQuery. However, the error disappears when I use the longer .ajax() method. I'm using CodeIgniter as the server-side framework.

What's the reason for the error with the shorthand syntax, and why does the .ajax() method work?

form

<form id="add_category_form" action="<?= base_url("CategoriesController/process_add_category") ?>" method="post" enctype="multipart/form-data">
    <input type="hidden" name="<?= $this->security->get_csrf_token_name() ?>" value="<?= $this->security->get_csrf_hash() ?>" />
    <h2>Add a Category</h2>
    <ul>
        <li>
            <input type="text" name="name" required>
            <label>Category Name</label>
        </li>
        <li>
            <textarea name="description" required></textarea>
            <label>Description</label>
        </li>
        <label>Upload Images (5 Max)</label>
        <input type="file" name="image" id="category_image" accept="image/*">
    </ul>
    <!-- FIXME: center the content of this last li tag -->
    <button type="button" data-dismiss="modal" aria-label="Close">Cancel</button>
    <button type="submit">Save</button>
</form>

Using $.post

$(document).ready(function() {
  $("#add_category_form").submit(function(e) {
    e.preventDefault();
    let url = $(this).attr("action");

    if ($("#category_image").val() == "") {
      alert("Please add an image");
    } else {
      let formData = new FormData(this); // Pass the form element

      $.post(url, formData, function(response) {
        console.log(response);
        csrfName = response.csrfName;
        $("input[name='<?= $this->security->get_csrf_token_name() ?>']").val(response.newCsrfToken);
      }, "json");
    }
    return false;

using $.ajax

$(document).ready(function() {
  $("#add_category_form").submit(function(e) {
    e.preventDefault();
    if ($("#category_image").val() == "") {
      alert("Please add an image");
    }

    let formData = new FormData(this);
    $.ajax({
      url: "<?= base_url(''); ?>CategoriesController/process_add_category",
      type: "POST",
      data: formData,
      processData: false,
      contentType: false,
      dataType: "json",
      success: function(response) {
        console.log(response);
        csrfName = response.csrfName;
        $("input[name='<?= $this->security->get_csrf_token_name() ?>']").val(response.newCsrfToken);
      },
      error: function(xhr, status, error) {
        console.error("AJAX Error:", status, error);
      }
    });

    return false;
  });
});

Solution

  • You can't use a FormData object for the data when using the shorthand version of $.post(). The documentation says that the second argument must be either a plain object or a string, not FormData.

    You need to use a settings object just like $.ajax() so you can specify processData: false. Without this option, it tries to convert the object to a URL-encoded string.