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.

\$\endgroup\$
  • \$\begingroup\$ What code do you want reviewed here? The HTML? The (missing) PHP, or the JS code? \$\endgroup\$ – Elias Van Ootegem Oct 6 '14 at 15:20
  • \$\begingroup\$ @EliasVanOotegem the js code, php code is not missing I just var dump the post \$\endgroup\$ – Almis Oct 6 '14 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\$ – bumperbox Oct 6 '14 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\$ – Almis Oct 6 '14 at 17:59
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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.