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?
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: How to pass an array within a query string?
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']);
Or use @Curios's method
Sending 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']);
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);
public static
with function
+ remove the types declarations : any
, : FormData
and : FormData
.
if
block at line 7 with: if (!model.hasOwnProperty(propertyName) || (!model[propertyName] && model[propertyName]!==0 && model[propertyName]!=='')) continue;
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
(value !== null) && formData.append(key, value)
instead of just formData.append(key, value)
otherwise it fails on null values
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]) ;
}
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);
...
}
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;
}
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);
}
}
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
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
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;
}
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]
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)
simple way :
data.map(dt=>formdata.append("name",dt))
i've tried it, it works perfectly
Success story sharing
arr[]
informData.append('arr[]', arr[i]);
? why isn'tarr
correct? I tried both but onlyarr[]
worked.arr
your just redefine this value on each loop iteration, and in the end, the final value would equal to last array element, but not the whole arrayarr[]
, why isn'tarr[]
redefined?arr[]
is also a string . And while testing both neitherarr
norarr[]
was redefined in my case. I got multiple array in FormData with same key but different value. So i gotarr
with value1
and anotherarr
with value2
.arr
also worked for arrays. In this topic there is a more detailed answer to this questionfor (var i = 0; i < myArr; i++) { var myItemInArr = myArr[i]; for (var prop in myItemInArr) { fileData.append(`myArr[${i}][${prop}]`, myItemInArr[prop]); } }