appending array to FormData and send via AJAX – Ajax

Photo of author
Written By M Ibrahim
ajax arrays form-data

Quick Fix: For sending an array through FormData, stringify it in JavaScript, then parse it in PHP. Alternatively, you can use a custom delimiter to serialize and deserialize the array.

The Problem:

You’re using AJAX to send a multipart form containing an array named ‘arr’. However, when received on the PHP side, the ‘arr’ variable is interpreted as a string instead of an array. You want to be able to send both the ‘arr’ array and file inputs as part of the multipart form, but the current approach is causing the ‘arr’ array to be treated as a string due to the way it’s appended to the FormData object.

The Solutions:

Solution 1: Convert it to a JSON string, then parse it in PHP (recommended)

JS

var json_arr = JSON.stringify(arr);

PHP

$arr = json_decode($_POST['arr']);

Or use @Curios’s method

Sending an array via FormData.


JS

// Use <#> or any other delimiter you want
var serial_arr = arr.join("<#>"); 

PHP

$arr = explode("<#>", $_POST['arr']);

Solution 3: Utilizing a Utility Class for Converting Model to Form Data

The provided TypeScript solution leverages a utility class named Utility to convert a model or object into a FormData instance, enabling you to send arrays, text fields, and files.

Here’s a breakdown of how it works:

  1. Creating the Utility Class:

    The class is defined as follows:


    export class Utility {
      public static convertModelToFormData(model: any, form: FormData = null, namespace = ''): FormData {
        let formData = form || new FormData();
        let formKey;

        for (let propertyName in model) {
          if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
          formKey = namespace ? ${namespace}[${propertyName}] : propertyName;
          if (model[propertyName] instanceof Date)
            formData.append(formKey, model[propertyName].toISOString());
          else if (model[propertyName] instanceof Array) {
            model[propertyName].forEach((element, index) => {
              const tempFormKey = ${formKey}[${index}];
              this.convertModelToFormData(element, formData, tempFormKey);
            });
          }
          else if (typeof model[propertyName] === 'object' && !(model[propertyName] instanceof File))
            this.convertModelToFormData(model[propertyName], formData, formKey);
          else
            formData.append(formKey, model[propertyName].toString());
        }
        return formData;
      }
    }

  2. Function Parameters:
    • model: This is the object or model whose properties you want to convert into FormData.
    • form (optional): You can pass an existing FormData instance here if you want to append data to it. If not provided, a new FormData instance will be created.
    • namespace (optional): This is used to specify a namespace for the form data. For example, if you have nested objects, you can use this to create a hierarchical structure in the FormData.
  3. Iterating through Model Properties:

    The class iterates through the properties of the model object using a for...in loop.

    For each property, it checks if it exists and has a value. If it does, it extracts the property name and determines the formKey based on the namespace (if provided).

  4. Handling Different Property Types:

    The class handles different property types in the following manner:

    • Dates: If the property value is an instance of Date, it converts it to an ISO string format using toISOString() before appending it to the FormData.
    • Arrays: If the property value is an array, it iterates through the array items and recursively calls convertModelToFormData() for each item. This ensures that nested arrays are also converted correctly.
    • Objects: If the property value is an object that is not a File, it recursively calls convertModelToFormData() for that object, allowing you to handle nested objects.
    • Other Types: For other types, such as strings, numbers, and booleans, it simply converts them to strings and appends them to the FormData.
  5. Usage:

    To use this class, you would typically call the convertModelToFormData() method, passing your model as the first argument. It returns a FormData instance that you can then send via AJAX or use for other purposes.

    For instance:


    let formData = Utility.convertModelToFormData(model);

By utilizing this Utility class, you gain a powerful and flexible way to convert your models into FormData instances, enabling you to send complex data, including arrays, text fields, and files, via AJAX or other mechanisms.

Solution 4: Using a recursive function to convert object to FormData.

To resolve the issue, convert the object into a FormData object containing arrays and child objects. The following recursive function walks through the object and creates the correct formData object:

function getFormData(formData, data, previousKey) {
  if (data instanceof Object) {
    Object.keys(data).forEach(key => {
      const value = data[key];
      if (value instanceof Object && !Array.isArray(value)) {
        return this.getFormData(formData, value, key);
      }
      if (previousKey) {
        key = `${previousKey}[${key}]`;
      }
      if (Array.isArray(value)) {
        value.forEach(val => {
          formData.append(`${key}[]`, val);
        });
      } else {
        formData.append(key, value);
      }
    });
  }
}

In the example provided, the JSON is:

{
  name: 'starwars',
  year: 1977,
  characters: {
    good: ['luke', 'leia'],
    bad: ['vader'],
  },
}

After passing it through the `getFormData` function, it’s converted to the following FormData:

name, starwars
year, 1977
characters[good][], luke
characters[good][], leia
characters[bad][], vader

Solution 5: Append all type inputs to FormData

Instead of appending variables to data one by one, create a FormData object and add all inputs to it. The code below will loop through each input field in the form and append its value to the FormData object:

const formData = new FormData();
for (let key in form) {
    Array.isArray(form[key])
        ? form[key].forEach(value => formData.append(key + '[]', value))
        : formData.append(key, form[key]) ;
}

Then, you can use the FormData object as the data parameter in your AJAX request. This will allow you to send all of the form inputs, including arrays, to your PHP file.