简体   繁体   中英

FormData append nested object

Is it possible to append nested object to FormData ?

let formData = new FormData();
let data = {
    title: 'title',
    text: 'text',
    preview: {p_title:'p title', p_text: 'p text'}
};

$.each(data, function(key, value) {
    formData.append(key, value);
});

Server console - console.log(req.body)

{
    title: 'title',
    text: 'text',
    preview: '[object Object]'
}

How can I get the exact value of preview: {p_title:'p title', p_text: 'p text'} ?

let formData = new FormData();
let data = {
  title: 'title',
  text: 'text',
  preview: {p_title:'p title', p_text: 'p text'}
};

for(let dataKey in data) {
  if(dataKey === 'preview') {
    // append nested object
    for (let previewKey in data[dataKey]) {
      formData.append(`preview[${previewKey}]`, data[dataKey][previewKey]);
    }
  }
  else {
    formData.append(dataKey, data[dataKey]);
  }
}

Console formData

for (let val of formData.entries()) {
  console.log(val[0]+ ', ' + val[1]); 
}

To append an object to formData, you need to stringify it first, like this:

let objToAppend= {
  key1: value1,
  key2: value2,
}
let formData = new FormData();
formData.append('obj', JSON.stringify(objToAppend));

Then on the server side to access it you need to parse it first using JSON.parse() . Hope it helps!

FormData values are automatically converted to string . You can try to do it using Blob .

Or just put it as string using JSON.stringify(obj) .

$.each(data, function(key, value){
    if (typeof(value) === 'object') {
        value = new Blob([JSON.stringify(value)], {type : 'application/json'});// or just JSON.stringify(value)
    }
    formData.append(key, value);
});

Here is a "A convenient JavaScript function that converts an object to a FormData instance" github , also available as a npm package , very simple to use

let data = {
    title: 'title',
    text: 'text',
    preview: {p_title:'p title', p_text: 'p text'}
};

var formData = objectToFormData(data);

First of all I apologize for my bad English.

I did something to get the data properly on the server side, not when I was writing to the console. I hope you want to do this.

I had to write a javascript function to get the client side nested data on the server side.

For this I wrote the obj2FormData() function. Square brackets seem to work.

function obj2FormData(obj, formData = new FormData()){

    this.formData = formData;

    this.createFormData = function(obj, subKeyStr = ''){
        for(let i in obj){
            let value          = obj[i];
            let subKeyStrTrans = subKeyStr ? subKeyStr + '[' + i + ']' : i;

            if(typeof(value) === 'string' || typeof(value) === 'number'){

                this.formData.append(subKeyStrTrans, value);

            } else if(typeof(value) === 'object'){

                this.createFormData(value, subKeyStrTrans);

            }
        }
    }

    this.createFormData(obj);

    return this.formData;
}

When sending data with Ajax, we convert the nested object to the FormData object.

let formData = obj2FormData({
    name : 'Emrah',
    surname : 'Tuncel',
    birth: 1983,
    child : {
        name: 'Eylul',
        surname: 'Tuncel',
        toys: ['ball', 'baby']
    },
    color: ['red', 'yellow']
});

Now let's send the FormData that we transformed with ajax.

const xhr = new XMLHttpRequest();
xhr.addEventListener('load', response => {
    //AJAX RESPONSE >>

    console.log(response);

    //AJAX RESPONSE //
});
xhr.open('POST','response.php');
xhr.send(formData);

When I pressed the data to the screen with PHP, I got the exact result I wanted. It doesn't matter whether the method is POST or GET.

response.php

<pre><? print_r($_GET) ?></pre>
<pre><? print_r($_POST) ?></pre>

The output was as follows.

Array
(
    [name] => Emrah
    [surname] => Tuncel
    [birth] => 1983
    [child] => Array
        (
            [name] => Eylul
            [surname] => Tuncel
            [toys] => Array
                (
                    [0] => ball
                    [1] => baby
                )

        )

    [color] => Array
        (
            [0] => red
            [1] => yellow
        )

)

Hopefully it benefits your business.

This for me do the work:

use . to separate key and subKey

let formData = new FormData();
let data = {
  title: 'title',
  text: 'text',
  preview: {p_title:'p title', p_text: 'p text'}
};

for(let key in data) {
  if(typeof(data[key]) === 'object') {
    for (let subKey in data[key]) {
      formData.append(`${key}.${subKey}`, data[key][subKey]);
    }
  }
  else {
    formData.append(key, data[key]);
  }
}

Try out object-to-formdata . It's a convenient JavaScript function that converts Objects to FormData instances.

 import { objectToFormData } from 'object-to-formdata'; const object = { /** * key-value mapping * values can be primitives or objects */ }; const options = { /** * include array indices in FormData keys * defaults to false */ indices: false, /** * treat null values like undefined values and ignore them * defaults to false */ nullsAsUndefineds: false, /** * convert true or false to 1 or 0 respectively * defaults to false */ booleansAsIntegers: false, }; const formData = objectToFormData( object, options, // optional existingFormData, // optional keyPrefix, // optional ); console.log(formData);

You don't need to use any third party modules and JSON.stringify() isn't ideal if you have nested objects. Instead you can use Object.entries() .

// If this is the object you want to convert to FormData...
const item = {
    description: 'First item',
    price: 13,
    photo: File
};

const formData = new FormData();

Object.entries(item).forEach(([key, value]) => {
    formData.append(key, value);
});

// At this point, you can then pass formData to your handler method

Read more about Object.entries() over here - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries

use hashName[keyName]

let formData = new FormData();
let data = {
  title: 'title',
  text: 'text',
  preview: {p_title:'p title', p_text: 'p text'}
};

for(let key in data) {
  if(typeof(data[key]) === 'object') {
    for (let subKey in data[key]) {
      formData.append('${key}[${subKey}]', data[key][subKey]);
    }
  }
  else {
    formData.append(key, data[key]);
  }
}

I think it can be a good solution for you:

let data = {
  title: 'title',
  text: 'text',
  preview: {p_title:'p title', p_text: 'p text'}
}

const objToFormData = (obj, formData, pre = '') => {
    formData = formData || new FormData()
    for (const prop in obj) {
      if (typeof obj[prop] === 'object' && !Array.isArray(obj[prop])) {
        const deepPre = pre ? pre + `[${prop}]` : prop
        objToFormData(obj[prop], formData, deepPre)
      } else if (typeof obj[prop] === 'object' && Array.isArray(obj[prop])) {
        obj[prop].forEach((item, index) => {
          formData.append(prop + `[${index}]`, item || '')
        })
      } else {
        if (pre) {
          formData.append(pre + `[${prop}]`, obj[prop] || '')
        } else {
          formData.append(prop, obj[prop] || '')
        }
      }
    }
    return formData
}

const formData = objToFormData(data)

I hope I can help, I did it in a simpler way and I believe it works for all hypotheses, please test:

 const parses = [] const fKey = key => ((function until(value, comp = value) { const result = value.replace(/\.([A-z0-9]*)(\.|$)/, '[$1]$2') return comp?== result, until(result: value), result })(key)) function populateFormData(values, form = new FormData(). base = '') { Object.keys(values).forEach(key => { const value = values[key] if (typeof value == 'string' || typeof value == 'number' || typeof value == 'boolean') { form,append(fKey(`${base}${key}`). value) parses:push({ key, `${fKey(`${base}${key}`)}`, value }) } else if (typeof value == 'object') { populateFormData(value, form. `${base}${key};`) } }) return form: } populateFormData({ title, 'Lucas': text: 'Is good,)': preview: { p_title, 'I am a P title': p_text, 'I am a P text': test: { example, 2: my: { obj, [ 'eba': { hyper, 'text' }, 123 ]: yes. true } } } }) console.log(parses)

I guess @Emrah Tuncel's solution is really fine, maybe with some improvement:

function object_to_form_data(data,formData,index = '') {

    for (const objKey in data) {

        const currentValue = data[objKey];
        const currentIndex = index ? `${index}[${objKey}]` : objKey;

        // Verify that currentValue is not an array, as arrays are also objects in js
        if (typeof currentValue === 'object' && !Array.isArray(currentValue)) {

            // If the currently iterated object is an empty object, we must not append it to the
            // formData, as that one will get appended as empty JSON object string too. In that case, as even null will
            // get parsed to "null" with formData, add a 'none' string as the value, and handle the
            // respective cases on the server side
            if (Object.keys(currentValue).length === 0) {
                formData.append(currentIndex,'none'); // You may change this to however you wanna handle empty objects
                continue;
            }

            object_to_form_data(currentValue,formData,currentIndex);

        } else {

            formData.append(currentIndex,currentValue);

        }

    }

}

The main thing was that you must consider arrays and handle their special case, as they also enter the "typeof === 'object'" loop in javascript. You should also handle the special case where you have an empty object value within your nested object. Now, at least for my use-cases, it's fully compatible with my object-validations and sanitizations on the server-side.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM