4
\$\begingroup\$

Let's say I have this form:

<form action="/user/store" method="POST" enctype="multipart/form-data">
 Name: <input type="text" name="name"><br>
 Male: <input type="radio" name="gender" value="male"><br>
 Female: <input type="radio" name="gender" value="female"><br>
 Photo: <input type="file" name="photo"><br>
 Hobbies: <select name="hobbies[]" multiple>
 <option value="sport">Sport</option>
 <option value="movies">Movies</option>
 <option value="music">Music</option>
 <option value="games">Games</option>
 </select><br>
 Accept the terms?<input type="checkbox" name="terms"><br>
 <input type="submit">
</form>

If I submit it to a PHP server with my data, this is the result:

array (size=5)
 'name' => string 'John' (length=4)
 'gender' => string 'male' (length=4)
 'hobbies' => 
 array (size=2)
 0 => string 'sport' (length=5)
 1 => string 'music' (length=5)
 'terms' => string 'on' (length=2)
 'photo' => 
 object(Symfony\Component\HttpFoundation\File\UploadedFile)[9]
 private 'test' => boolean false
 private 'originalName' => string 'test.jpg' (length=8)
 private 'mimeType' => string 'image/jpeg' (length=10)
 private 'size' => int 130677
 private 'error' => int 0

I want to have similar results with JavaScript, so I wrote this function:

//Add event listener for the form submit to the document
$(document).on('submit', 'form', function(e){
 //Prevent form submitting
 e.preventDefault();
 var form = $(':input').not(':submit,:button,:image,:radio,:checkbox');
 $.merge(form, $(':checked').not('option'));
 var data = [], input, name;
 form.each(function(i, obj){
 input = {};
 name = obj.name;
 if(name.indexOf('[]') < 0) {
 input['name'] = name;
 if(obj.type==='file'){
 var files = obj.files;
 input['value'] = files[files.length - 1];
 } else {
 input['value'] = obj.value;
 }
 } else {
 name = name.replace('[]', '');
 if(name in data){
 var tmp = input['value'];
 if(obj.type==='file'){
 input['value'] = $.merge(tmp, obj.files);
 } else if(obj.type === 'select-multiple') {
 input['value'] = $.merge(tmp, $.map(obj.selectedOptions, function(option, i){
 return option.value;
 }));
 } else {
 input['value'] = $.merge(tmp, obj.value);
 }
 } else {
 data[name] = {};
 if(obj.type === 'file'){
 input['value'] = obj.files;
 } else if(obj.type === 'select-multiple') {
 input['value'] = $.map(obj.selectedOptions, function(option, i){
 return option.value;
 });
 } else {
 input['value'] = [];
 input['value'].push(obj.value);
 }
 }
 }
 data.push(input);
 });
});

And this return (JSON format):

[
 {"name":"firstname","value":"John"},
 {"name":"photo","value":
 {
 "webkitRelativePath":"",
 "lastModifiedDate":"2014-09-08T18:29:36.000Z",
 "name":"test.jpg",
 "type":"image/jpeg","size":130677
 }
 },
 {"name":"hobbies","value":["sport","music"]},
 {"name":"gender","value":"male"},
 {"name":"terms","value":"on"}
]

Is there any better way to do that? By better code, I mean less code. I don't mind if it will become slower.

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Oct 6, 2014 at 15:07
\$\endgroup\$
4
  • \$\begingroup\$ What code do you want reviewed here? The HTML? The (missing) PHP, or the JS code? \$\endgroup\$ Commented Oct 6, 2014 at 15:20
  • \$\begingroup\$ @EliasVanOotegem the js code, php code is not missing I just var dump the post \$\endgroup\$ Commented Oct 6, 2014 at 15:23
  • \$\begingroup\$ It's not exactly what you have, but I generally use serialize or serializeArray to submit a forms via jquery. Have a look at api.jquery.com/serializearray \$\endgroup\$ Commented Oct 6, 2014 at 17:29
  • \$\begingroup\$ @bumperbox I dont do this to submit the form but to make validation, it's not handle the arrays as I want and not add the file to the array \$\endgroup\$ Commented Oct 6, 2014 at 17:59

1 Answer 1

1
\$\begingroup\$

I’m not sure if you still maintain this code but if so, you could consider using a FormData object. I see it has been documented on MDN since 2015 and the link to the specification points to whatwg.org, which has been archived since 2012. I found a post on SO from 2011 that uses it. That way you wouldn’t have to manually query the DOM for inputs (I.e. var form = $(':input').not(':submit,:button,:image,:radio,:checkbox'); $.merge(form, $(':checked').not('option'));)- you could just iterate over a newly created FormData object. You would still need to alter input names to remove the brackets when multiple values are allowed.

You might be able to remove jQuery as a dependency, unless there are specific plugins used on the page. For more information, check out youmightnotneedjquery.com/.


I see places where bracket notation is used to alter properties of objects, like below:

input['name'] = name;

and

input['value'] = files[files.length - 1];

However dot notation could be used instead, just as it is used to check the type of the object being iterated over (i.e. if(obj.type==='file'){). It is "faster to write and clearer to read" 1

1https://stackoverflow.com/a/4968448/1575353

answered Apr 26, 2019 at 15:23
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.