I don't know if this is relevant, but for my implementation, the following works great. After defining the empty array, it then has to loop through the actual array physically declaring the object recursively in order to cater for objects and sub-arrays etc...
formData.append(key + '[]', []);
So used in situ, my entire FormData build looks like this...
/* ............................................................ */
/* .........runPostFetchScripts Can be used throughout [AF].... */
function runPostFetchScripts(id, str) {
// Try and break out any scripts that need to also be run from the newly acquired HTML body (these don't naturally get run)...
// Find ALL script tags (most basic form ONLY for now)...
//console.log(str);
scripts = str.split('<sc' + 'ript>');
// First element will be content, so remove it
if(scripts.length > 1) {
scripts.splice(0, 1);
one_biggy = "";
for(i = 0; i < scripts.length; ++i) {
// Then get rid of following content after end of script tag
scripter = scripts[i].split("</sc" + "ript>");
// And run what is left...
//console.log(i + ": " + scripter[0]);
one_biggy += scripter[0];
}
//console.log(one_biggy);
eval(one_biggy);
}
// Phew! This took way longer than expected [AF]!
}
/* ............................................................ */
/* .........New inclusion for FormData Validation and addition of Files, Multiple Files and Arrays to Form uploads via Ajax.... */
function genericBuildFormData(formData, data, previousKey) {
/*
console.log("genericBuildFormData(");
console.log(formData);
console.log(data);
console.log(previousKey);
//*/
if(data instanceof File) {
//console.log(previousKey + " file append...");
formData.append(previousKey, data);
} else if (data instanceof Object) {
var keys = Object.keys(data);
for(var k = 0; k < keys.length; ++k) {
var key = keys[k];
var value = data[key];
/*
console.log(k + " " + key);
console.log(!(value instanceof File));
console.log(value instanceof Object);
console.log(!(Array.isArray(value)));
console.log(value);
//*/
if(previousKey != '') {
key = previousKey + '[' + key + ']';
}
if (!(value instanceof File) && (value instanceof Object) && !(Array.isArray(value))) {
//console.log("Is Object, go again... " + key + "[]");
formData.append(key + '[]', []);
genericBuildFormData(formData, value, key);
} else {
if (Array.isArray(value)) {
//console.log(key + " is Array...");
// Define empty array first...
formData.append(key + '[]', []);
// Now loop through all array contents, accounting for embedded objects and sub-arrays...
for (var a = 0; a < value.length; ++a) {
//console.log(a);
genericBuildFormData(formData, value[a], key + '[' + a + ']');
}
} else {
//console.log(key + " append...");
formData.append(key, value);
}
}
}
} else {
//console.log(previousKey + ": " + data + " append...");
formData.append(previousKey, data);
}
}
/* ............................................................ */
/* .............genericAjaxCall Can be used throughout [AF].... */
function genericAjaxCall(GetPost, FullUrl, PostParams, Container, id, callback) {
/*
console.log("genericAjaxCall(...");
console.log(FullUrl);
console.log(PostParams);
console.log(Container);
console.log(id);
//*/
var GET_POST = GetPost.toUpperCase();
var jacks = {
url: FullUrl,
type: GET_POST,
async: true,
processData: false, // Due to contentType, assume WE have got the data right, so do not allow AJAX to find fault with what we have done (files and arrays will fail otherwise) [AF]
contentType: false, // Let Ajax work this out for us seeing as we are going to be mixing arrays, objects and files as well as primitives, so don't tell it anything [AF]
success: function(strResult) {
populateContainer = (Container ? document.getElementById(Container) : false);
//console.log(populateContainer);
if(populateContainer) {
//populateContainer.innerHTML = ""; // To drop possible scroll values
populateContainer.innerHTML = strResult;
//console.log(strResult);
if(callback != null) {
window.setTimeout(function() {
callback(id, strResult);
}, 100);
}
} else {
//console.log("ajax.strResult: " + FullUrl + " " + id + " = " + strResult);
if(callback != null) {
window.setTimeout(function() {
callback(id, strResult);
}, 100);
}
}
},
error: function(jqXHR, textStatus, errorThrown) {
populateContainer = document.getElementById(Container);
if(populateContainer) {
populateContainer.innerHTML = "Error: " + textStatus + " - " + errorThrown;
} else {
if(callback != null) {
window.setTimeout(function() {
callback(id, null, 'Error: ' + textStatus + ' - ' + errorThrown);
}, 100);
} else {
console.log('Error: ' + textStatus + ' - ' + errorThrown);
}
}
}
};
if(GET_POST == "POST") {
//console.log(PostParams);
// Use FormData for File Upload inclusion
var myFormData = new FormData();
genericBuildFormData(myFormData, PostParams, '');
jacks.processData = false;
jacks.contentType = false;
jacks.data = myFormData;
}
//console.log(jacks);
$.ajax(jacks);
}
And the calling code looks like this (it is recursive, so bear that in mind when considering the parameters sent to the genericBuildFormData function)...
var myFormData = new FormData();
genericBuildFormData(myFormData, PostParams, '');
The resulting data when viewed in chrome's "Network Headers" (on commit) looks something like this...
additional[]:
additional[0]: null
additional[1][id]: 7715
additional[1][ex]: Permanent Exclusion
additional[1][mi]: 1
additional[2]: null
additional[3]: null
additional[4]: null
... where array elements 0,2,3,4 are empty and element 1 is an embedded object.
Hope this helps. Shalom.
foreachover the array in PHP, then "missing" indexes aren't a real problem.