How can I can convert my JS Object to FormData
?
The reason why I want to do this is, I have an object that I constructed out of the ~100 form field valu
You can simply install qs
:
npm i qs
Simply import:
import qs from 'qs'
Pass object to qs.stringify()
:
var item = {
description: 'Some Item',
price : '0.00',
srate : '0.00',
color : 'red',
...
...
}
qs.stringify(item)
I might be a little late to the party but this is what I've created to convert a singular object to FormData.
function formData(formData, filesIgnore = []) {
let data = new FormData();
let files = filesIgnore;
Object.entries(formData).forEach(([key, value]) => {
if (typeof value === 'object' && !files.includes(key)) {
data.append(key, JSON.stringify(value) || null);
} else if (files.includes(key)) {
data.append(key, value[0] || null);
} else {
data.append(key, value || null);
}
})
return data;
}
How does it work?
It will convert and return all properties expect File objects that you've set in the ignore list (2nd argument. If anyone could tell me a better way to determine this that would help!) into a json string using JSON.stringify
. Then on your server you'll just need to convert it back into a JSON object.
Example:
let form = {
first_name: 'John',
last_name: 'Doe',
details: {
phone_number: 1234 5678 910,
address: '123 Some Street',
},
profile_picture: [object FileList] // set by your form file input. Currently only support 1 file per property.
}
function submit() {
let data = formData(form, ['profile_picture']);
axios.post('/url', data).then(res => {
console.log('object uploaded');
})
}
I am still kinda new to Http requests and JavaScript so any feedback would be highly appreciated!
Recursively
const toFormData = (f => f(f))(h => f => f(x => h(h)(f)(x)))(f => fd => pk => d => {
if (d instanceof Object) {
Object.keys(d).forEach(k => {
const v = d[k]
if (pk) k = `${pk}[${k}]`
if (v instanceof Object && !(v instanceof Date) && !(v instanceof File)) {
return f(fd)(k)(v)
} else {
fd.append(k, v)
}
})
}
return fd
})(new FormData())()
let data = {
name: 'John',
age: 30,
colors: ['red', 'green', 'blue'],
children: [
{ name: 'Max', age: 3 },
{ name: 'Madonna', age: 10 }
]
}
console.log('data', data)
document.getElementById("data").insertAdjacentHTML('beforeend', JSON.stringify(data))
let formData = toFormData(data)
for (let key of formData.keys()) {
console.log(key, formData.getAll(key).join(','))
document.getElementById("item").insertAdjacentHTML('beforeend', `<li>${key} = ${formData.getAll(key).join(',')}</li>`)
}
<p id="data"></p>
<ul id="item"></ul>
Sorry for a late answer, but I was struggling with this as Angular 2 currently does not support file upload. So, the way to do it was sending a XMLHttpRequest
with FormData
. So, I created a function to do it. I'm using Typescript. To convert it to Javascript just remove data types declaration.
/**
* Transforms the json data into form data.
*
* Example:
*
* Input:
*
* fd = new FormData();
* dob = {
* name: 'phone',
* photos: ['myphoto.jpg', 'myotherphoto.png'],
* price: '615.99',
* color: {
* front: 'red',
* back: 'blue'
* },
* buttons: ['power', 'volup', 'voldown'],
* cameras: [{
* name: 'front',
* res: '5Mpx'
* },{
* name: 'back',
* res: '10Mpx'
* }]
* };
* Say we want to replace 'myotherphoto.png'. We'll have this 'fob'.
* fob = {
* photos: [null, <File object>]
* };
* Say we want to wrap the object (Rails way):
* p = 'product';
*
* Output:
*
* 'fd' object updated. Now it will have these key-values "<key>, <value>":
*
* product[name], phone
* product[photos][], myphoto.jpg
* product[photos][], <File object>
* product[color][front], red
* product[color][back], blue
* product[buttons][], power
* product[buttons][], volup
* product[buttons][], voldown
* product[cameras][][name], front
* product[cameras][][res], 5Mpx
* product[cameras][][name], back
* product[cameras][][res], 10Mpx
*
* @param {FormData} fd FormData object where items will be appended to.
* @param {Object} dob Data object where items will be read from.
* @param {Object = null} fob File object where items will override dob's.
* @param {string = ''} p Prefix. Useful for wrapping objects and necessary for internal use (as this is a recursive method).
*/
append(fd: FormData, dob: Object, fob: Object = null, p: string = ''){
let apnd = this.append;
function isObj(dob, fob, p){
if(typeof dob == "object"){
if(!!dob && dob.constructor === Array){
p += '[]';
for(let i = 0; i < dob.length; i++){
let aux_fob = !!fob ? fob[i] : fob;
isObj(dob[i], aux_fob, p);
}
} else {
apnd(fd, dob, fob, p);
}
} else {
let value = !!fob ? fob : dob;
fd.append(p, value);
}
}
for(let prop in dob){
let aux_p = p == '' ? prop : `${p}[${prop}]`;
let aux_fob = !!fob ? fob[prop] : fob;
isObj(dob[prop], aux_fob, aux_p);
}
}
The other answers were incomplete for me. I started from @Vladimir Novopashin answer and modified it. Here are the things, that I needed and bug I found:
.prop
instead of [prop]
. For example, formData.append('photos[0][file]', file)
didn't work on google chrome, while
formData.append('photos[0].file', file)
workedThe following code should work on IE11 and evergreen browsers.
function objectToFormData(obj, rootName, ignoreList) {
var formData = new FormData();
function appendFormData(data, root) {
if (!ignore(root)) {
root = root || '';
if (data instanceof File) {
formData.append(root, data);
} else if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
appendFormData(data[i], root + '[' + i + ']');
}
} else if (typeof data === 'object' && data) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
if (root === '') {
appendFormData(data[key], key);
} else {
appendFormData(data[key], root + '.' + key);
}
}
}
} else {
if (data !== null && typeof data !== 'undefined') {
formData.append(root, data);
}
}
}
}
function ignore(root){
return Array.isArray(ignoreList)
&& ignoreList.some(function(x) { return x === root; });
}
appendFormData(obj, rootName);
return formData;
}
Try JSON.stringify function as below
var postData = JSON.stringify(item);
var formData = new FormData();
formData.append("postData",postData );