appending array to FormData and send via AJAX

JavascriptAjaxArraysForm Data

Javascript Problem Overview


I'm using ajax to submit a multipart form with array, text fields and files.

I append each VAR to the main data as so

var attachments = document.getElementById('files');	
var data= new FormData();

for (i=0; i< attachments.files.length; i++){
	data.append('file', attachments.files[i]);
	console.log(attachments.files[i]);

	data.append ('headline', headline);
	data.append ('article', article);
	data.append ('arr', arr);
	data.append ('tag', tag);

then I use the ajax function to send it to a PHP file to store inside sql DB.

$.ajax({    
    type: "post",
    url: 'php/submittionform.php',
    cache: false,
    processData: false,
    contentType: false,
    data: data,
    success: function(request) {$('#box').html(request); }
})

But on the PHP side, the arr variable, which is an array appears as a string.

When I don't send it with ajax as Form data but use the simple $.POST option I do get it as an array on the PHP side, but then I can't send the files as well.

any solutions?

Javascript Solutions


Solution 1 - Javascript

You can also send an array via FormData this way:

var formData = new FormData;
var arr = ['this', 'is', 'an', 'array'];

for (var i = 0; i < arr.length; i++) {
  formData.append('arr[]', arr[i]);
}

console.log(...formData);

So you can write arr[] the same way as you do it with a simple HTML form. In case of PHP it should work.

You may find this article useful: https://stackoverflow.com/questions/6243051/how-to-pass-an-array-within-a-query-string

Solution 2 - Javascript

You have several options:

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 - Javascript

Typescript version:

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;
			let 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;
	}
}

Using:

let formData = Utility.convertModelToFormData(model);

Solution 4 - Javascript

This is an old question but I ran into this problem with posting objects along with files recently. I needed to be able to post an object, with child properties that were objects and arrays as well.

The function below will walk through an object and create the correct formData object.

// formData - instance of FormData object
// data - object to post
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);
      }
    });
  }
}

This will convert the following json -

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

into the following FormData

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

Solution 5 - Javascript

add all type inputs to FormData

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]) ;
}

Solution 6 - Javascript

here's another version of the convertModelToFormData since I needed it to also be able to send Files.

utility.js

const Utility = {
  convertModelToFormData(val, formData = new FormData, namespace = '') {
    if ((typeof val !== 'undefined') && val !== null) {
      if (val instanceof Date) {
        formData.append(namespace, val.toISOString());
      } else if (val instanceof Array) {
        for (let i = 0; i < val.length; i++) {
          this.convertModelToFormData(val[i], formData, namespace + '[' + i + ']');
        }
      } else if (typeof val === 'object' && !(val instanceof File)) {
        for (let propertyName in val) {
          if (val.hasOwnProperty(propertyName)) {
            this.convertModelToFormData(val[propertyName], formData, namespace ? `${namespace}[${propertyName}]` : propertyName);
          }
        }
      } else if (val instanceof File) {
        formData.append(namespace, val);
      } else {
        formData.append(namespace, val.toString());
      }
    }
    return formData;
  }
}
export default Utility;

my-client-code.js

import Utility from './utility'
...
someFunction(form_object) {
  ...
  let formData = Utility.convertModelToFormData(form_object);
  ...
}

Solution 7 - Javascript

Next version valid for model containing arays of simple values:

function convertModelToFormData(val, formData = new FormData(), namespace = '') {
	if((typeof val !== 'undefined') && (val !== null)) {
		if(val instanceof Date) {
			formData.append(namespace, val.toISOString());
		} else if(val instanceof Array) {
			for(let element of val) {
				convertModelToFormData(element, formData, namespace + '[]');
			}
		} else if(typeof val === 'object' && !(val instanceof File)) {
			for (let propertyName in val) {
				if(val.hasOwnProperty(propertyName)) {
					convertModelToFormData(val[propertyName], formData, namespace ? namespace + '[' + propertyName + ']' : propertyName);
				}
			}
		} else {
			formData.append(namespace, val.toString());
		}
	}
	return formData;
}

Solution 8 - Javascript

If you have nested objects and arrays, best way to populate FormData object is using recursion.

function createFormData(formData, data, key) {
    if ( ( typeof data === 'object' && data !== null ) || Array.isArray(data) ) {
        for ( let i in data ) {
            if ( ( typeof data[i] === 'object' && data[i] !== null ) || Array.isArray(data[i]) ) {
                createFormData(formData, data[i], key + '[' + i + ']');
            } else {
                formData.append(key + '[' + i + ']', data[i]);
            }
        }
    } else {
        formData.append(key, data);
    }
}

Solution 9 - Javascript

Based on @YackY answer shorter recursion version:

function createFormData(formData, key, data) {
    if (data === Object(data) || Array.isArray(data)) {
        for (var i in data) {
            createFormData(formData, key + '[' + i + ']', data[i]);
        }
    } else {
        formData.append(key, data);
    }
}

Usage example:

var data = {a: '1', b: 2, c: {d: '3'}};
var formData = new FormData();
createFormData(formData, 'data', data);

Sent data:

data[a]=1&
data[b]=2&
data[c][d]=3

Solution 10 - Javascript

TransForm three formats of Data to FormData :

1. Single value like string, Number or Boolean

 let sampleData = {
  activityName: "Hunting3",
  activityTypeID: 2,
  seasonAssociated: true, 
};

2. Array to be Array of Objects

let sampleData = {
   activitySeason: [
    { clientId: 2000, seasonId: 57 },
    { clientId: 2000, seasonId: 57 },
  ],
};

3. Object holding key value pair

let sampleData = {
    preview: { title: "Amazing World", description: "Here is description" },
};

The that make our life easy:

function transformInToFormObject(data) {
  let formData = new FormData();
  for (let key in data) {
    if (Array.isArray(data[key])) {
      data[key].forEach((obj, index) => {
        let keyList = Object.keys(obj);
        keyList.forEach((keyItem) => {
          let keyName = [key, "[", index, "]", ".", keyItem].join("");
          formData.append(keyName, obj[keyItem]);
        });
      });
    } else if (typeof data[key] === "object") { 
      for (let innerKey in data[key]) {
        formData.append(`${key}.${innerKey}`, data[key][innerKey]);
      }
    } else {
      formData.append(key, data[key]);
    }
  }
  return formData;
}

Example : Input Data

let sampleData = {
  activityName: "Hunting3",
  activityTypeID: 2,
  seasonAssociated: true,
  activitySeason: [
    { clientId: 2000, seasonId: 57 },
    { clientId: 2000, seasonId: 57 },
  ],
  preview: { title: "Amazing World", description: "Here is description" },
};

Output FormData :

activityName: Hunting3
activityTypeID: 2
seasonAssociated: true
activitySeason[0].clientId: 2000
activitySeason[0].seasonId: 57
activitySeason[1].clientId: 2000
activitySeason[1].seasonId: 57
preview.title: Amazing World
preview.description: Here is description

Solution 11 - Javascript

I've fixed the typescript version. For javascript, just remove type definitions.

  _getFormDataKey(key0: any, key1: any): string {
    return !key0 ? key1 : `${key0}[${key1}]`;
  }
  _convertModelToFormData(model: any, key: string, frmData?: FormData): FormData {
    let formData = frmData || new FormData();

    if (!model) return formData;

    if (model instanceof Date) {
      formData.append(key, model.toISOString());
    } else if (model instanceof Array) {
      model.forEach((element: any, i: number) => {
        this._convertModelToFormData(element, this._getFormDataKey(key, i), formData);
      });
    } else if (typeof model === 'object' && !(model instanceof File)) {
      for (let propertyName in model) {
        if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
        this._convertModelToFormData(model[propertyName], this._getFormDataKey(key, propertyName), formData);
      }
    } else {
      formData.append(key, model);
    }

    return formData;
  }

Solution 12 - Javascript

JavaScript code:

var formData = new FormData();
let arr = [1,2,3,4];
formData.append('arr', arr);

Output on php:

$arr = $_POST['arr']; ===>  '1,2,3,4'

Solution php code:

$arr = explode(",", $_POST['arr']); ===> [1,2,3,4]

Solution 13 - Javascript

In my case I got

{
    "category": [
        "Incorrect type. Expected pk value, received str."
    ]
}

error so

For who one use React (frontend) and Django DRF (backend):


const category = [1,2,3,4,5]
formData.append("category", JSON.stringify(category));

import json
from .serializers import ArticleSerializer
from rest_framework.response import Response

class ArticleListCreateView(ListCreateAPIView):
	permission_classes = (
		IsSuperuserOrReadOnly,
		IsAutherOrReadOnly,
	)
	serializer_class = ArticleSerializer
	model = Article
	queryset = Article.objects.filter(article_honor='a', status='p')


	def create(self, request, *args, **kwargs):
		data = request.data
		article = Article('set all argumenst')
		
		image = request.data['image']
		# save image in local

		article.save()
		categorys = json.loads(data['category'])
		for catID in categorys:
			cat = Category.objects.get(id=catID)
			article.category.add(cat)

        article.save()

		return Response(ArticleSerializer(article).data)


Solution 14 - Javascript

simple way :

data.map(dt=>formdata.append("name",dt))

i've tried it, it works perfectly

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionshultzView Question on Stackoverflow
Solution 1 - JavascriptOlegView Answer on Stackoverflow
Solution 2 - JavascriptRichard de WitView Answer on Stackoverflow
Solution 3 - JavascriptMohammad DayyanView Answer on Stackoverflow
Solution 4 - JavascriptVtoCorleoneView Answer on Stackoverflow
Solution 5 - JavascriptHamidNEView Answer on Stackoverflow
Solution 6 - JavascriptZombiesplatView Answer on Stackoverflow
Solution 7 - JavascriptMegabyteView Answer on Stackoverflow
Solution 8 - JavascriptYackYView Answer on Stackoverflow
Solution 9 - JavascriptdikirillView Answer on Stackoverflow
Solution 10 - JavascriptmabdullahseView Answer on Stackoverflow
Solution 11 - JavascriptkayaView Answer on Stackoverflow
Solution 12 - JavascriptRouani AyoubView Answer on Stackoverflow
Solution 13 - JavascriptNimaView Answer on Stackoverflow
Solution 14 - JavascriptBlueView Answer on Stackoverflow