appending array to FormData and send via AJAX
JavascriptAjaxArraysForm DataJavascript 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:
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']);
@Curios's method
Or useSending an array via FormData
.
Not recommended: Serialize the data with, then deserialize in PHP
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