Bug Tracker

Opened 12 years ago

Closed 11 years ago

Last modified 8 years ago

#2207 closed feature (wontfix)

serializeArray should treat elements with [braces] as arrays

Reported by: dubhunter Owned by: flesler
Priority: major Milestone: 1.3
Component: ajax Version: 1.2.6
Keywords: serialize array Cc:
Blocked by: Blocking:

Description

serializeArray should treat elements with [braces] as arrays.

I understand that serialize (which returns a get query string) needs the [] braces left by serializeArray, but since on the receiving end, the query string would be parsed back into an array....shouldn't serializeArray do the same?

Could it be that $().val() never returns type of array?

I've tried with checkboxes, selects, select multiples... and I cannot get it to figure things out.

Change History (10)

comment:1 Changed 12 years ago by davidserduke

Type: bugfeature

comment:2 Changed 12 years ago by flesler

Owner: set to flesler
Status: newassigned

This sounds like a fun feature, let me handle :)

comment:3 Changed 12 years ago by flesler

OK, I think I missunderstood this, I thought it was something like #2726. But seems it isn't.

Could you please rephrase your ticket a bit ? Thanks

comment:4 Changed 12 years ago by ygirouard

I see what he means: If you have multiple inputs with names like "check[1]", "check[2]", "check[3]", etc...

The $.serializeArray() function should convert those inputs into an array of "check" as it would be done by server-side parser when the data is passed via POST or GET...

But instead it creates an array like this:

array(
 0 => array("check[1]" => value1),
 1 => array("check[2]" => value2),
 2 => array("check[3]" => value3)
)

instead of...

array(
 0 => array("check" =>  array("1" => value1, "2" => value2, "3" => value3))
)

... which is what passing inputs that have braces in their names would become after being passed with POST or GET once they get parsed by the server.

I believe it does fall within the same subject as the closed ticket you referenced however.

comment:5 Changed 11 years ago by flesler

Component: coreajax
Milestone: 1.2.31.3

comment:6 Changed 11 years ago by flesler

Resolution: worksforme
Status: assignedclosed

This does work for me, see #2377.

This html:

<form>
	<input type="text" name="name[0]" value="aaaa" />
	<input type="text" name="name[1]" value="bbbb" />
	<input type="text" name="name[2]" value="cccc" />
</form>

With this PHP:

<?php
	print_r($_POST);
?>

I ran this code:

$.ajax({ url:'foo.php', data:$('form').serialize(), type:'post'});

And the response was:

Array(
    [name] => Array(
      [0] => aaaa
      [1] => bbbb
      [2] => cccc
    )
)

Tried this with Firefox 2, both POST and GET.

Do reopen this with a concise test case that reproduces the error.

comment:7 Changed 11 years ago by dubhunter

Resolution: worksforme
Status: closedreopened

OK, this took me a while to remember what is going on.

You are right, your example works perfectly...but only because there is an extra step.

When the serializeArray() return is passed as the data arg for $.ajax();, it is then passed through $.param(); which does the dirty work. This is fine when always using it for form submissions, but what I am after is just the serializedArray, not a array of strings to be complied into a get string.

The problem is on the client side, not the PHP side. Except it does become a PHP problem if I am passing data to the server without using $.ajax(); or $.param();

My Solution to this problem has been to create a method called $.serializeAssoc(); It will recursively serialize heterogeneous form arrays into a neat array like package. (I say array like, since JS will not support associative arrays, but objects work almost as well.)

I have attached my markup, js, php output, and my extension file (includes logging function and $.serializeAssoc()). I did not inlcude the debug output, because its hard to copy from firebug.

ygirouard is right though on the format of the JS object returned by $.serializeArray();

Here is my markup:

	<form name="jquery" id="jquery" action="/index/jqueryPost" method="post">
		<input type="checkbox" name="check[]" id="check0" value="aaaa" />
		<input type="checkbox" name="check[]" id="check1" value="bbbb" />
		<input type="checkbox" name="check[]" id="check2" value="cccc" />
		<input type="submit" name="button" id="button" value="submit" />
	</form>

Here is the JS:

$(function (){
	$('form :submit').click(function (e){
		e.preventDefault();
		var f = $(this).parents('form').get(0);

		$.log(f.action);

		var data = $(f).find(':input').serialize();
		data.fun = 'serialize';
		$.log(data);
		$.ajax({url: f.action, data: data, type: 'post', success: function (data){$.log(data);}});

		var data = $(f).find(':input').serializeArray();
		data.fun = 'serializeArray';
		$.log(data);
		$.ajax({url: f.action, data: data, type: 'post', success: function (data){$.log(data);}});

		var data = $(f).find(':input').serializeAssoc();
		data.fun = 'serializeAssoc';
		$.log(data);
		$.ajax({url: f.action, data: data, type: 'post', success: function (data){$.log(data);}});
	}).addClass("formBound");
});

And the responses:

Array
(
    [check] => Array
        (
            [0] => aaaa
            [1] => bbbb
            [2] => cccc
        )

)
Array
(
    [check] => Array
        (
            [0] => aaaa
            [1] => bbbb
            [2] => cccc
        )

)
Array
(
    [check] => Array
        (
            [0] => aaaa
            [1] => bbbb
            [2] => cccc
        )
    [fun] => serializeAssoc
)

Here is my JQ Lib file (jquery.ext.js):

(function($){
	$.extend({
		cssFile: function (f, m){
			$('<link>')
				.attr('href', f)
				.attr('type', 'text/css')
				.attr('rel', 'stylesheet')
				.attr('media', m || 'screen')
				.appendTo('head');
		},
		isArray: function (arr){
			if (typeof arr == 'object'){
				if (arr.constructor == Array){
					return true;
				}
			}
			return false;
		},
		arrayMerge: function (){
			var a = {};
			var n = 0;
			var argv = $.arrayMerge.arguments;
			for (var i = 0; i < argv.length; i++){
				if ($.isArray(argv[i])){
					for (var j = 0; j < argv[i].length; j++){
						a[n++] = argv[i][j];
					}
					a = $.makeArray(a);
				} else {
					for (var k in argv[i]){
						if (isNaN(k)){
							var v = argv[i][k];
							if (typeof v == 'object' && a[k]){
								v = $.arrayMerge(a[k], v);
							}
							a[k] = v;
						} else {
							a[n++] = argv[i][k];
						}
					}
				}
			}
			return a;
		},
		count: function (arr){
			if ($.isArray(arr)){
				return arr.length;
			} else {
				var n = 0;
				for (var k in arr){
					if (!isNaN(k)){
						n++;
					}
				}
				return n;
			}
		},
		log: function (msg){
			if (window.console)
				console.log(msg);
			else
				alert(msg);
		}
	});
	$.fn.extend({
		serializeAssoc: function (){
			var o = {
				aa: {},
				add: function (name, value){
					var tmp = name.match(/^(.*)\[([^\]]*)\]$/);
					if (tmp){
						var v = {};
						if (tmp[2])
							v[tmp[2]] = value;
						else
							v[$.count(v)] = value;
						this.add(tmp[1], v);
					}
					else if (typeof value == 'object'){
						if (typeof this.aa[name] != 'object'){
							this.aa[name] = {};
						}
						this.aa[name] = $.arrayMerge(this.aa[name], value);
					}
					else {
						this.aa[name] = value;
					}
				}
			};
			var a = $(this).serializeArray();
			for (var i = 0; i < a.length; i++){
				o.add(a[i].name, a[i].value);
			}
			return o.aa;
		}
	});
})(jQuery);

comment:8 Changed 11 years ago by flesler

need: ReviewTest Case
Version: 1.2.21.2.6

Ok, I see your function... Now, both serialize() and serializeArray() are meant for server side posting. The way the array is serialized by param() is perfectly understood by the servers.

If you need a different serialization, for client side, you should indeed write your own.

Unless you can show a concrete situation (with a test case) where this is actually needed for ajax requests, this won't be taken into account.

I'll leave this open for now, please reply.

comment:9 Changed 11 years ago by dubhunter

I guess I just see a lot of value in the ability to work with the data structure before its sent to the server or not to send it to the server at all. This is essentially why I originally list the component to be the "core" and not "ajax" because I agree that that functionality works perfectly.

My dream scenario would be for serializeArray() to return the proper data structure and then have param() take care of the rest when using it in an ajax context. Otherwise why even expose serializeArray(), when serialize() does everything you need for server-side processing.

I am personally fine with using my own function, but I just see value in this for other people.

While you are at it...any chance on getting any of my other methods into the core? cssFile(), isArray(), arrayMerge(), count(), log()???

comment:10 Changed 11 years ago by flesler

Resolution: wontfix
Status: reopenedclosed

The answer is always the same, if you provide a test case where any of these functions help improve perfomance or reduce code size, they will be added. If they're just useful for the common user, but not for the core, then no. They can always be put into a plugin.

My opinion: cssFile: it's cool but has no use for the core. isArray: we just use obj.constructor == Array. ArrayMerge: Seems like a special case of $.extend. We use that method and for now, no complaints. console & log: definitely useful, but not for the core.

Don't get me wrong, I like your code, but we're really cautious about new features. That's how we keep the filesize low.

Cheers

Note: See TracTickets for help on using tickets.