HTML array + AJAX

Hi,

I have a some php code that creates an HTML form with some arrays:


<input class="ins_opd" id="opdracht_titel" name="opdracht_titel['.$opdracht.']" type="text"  value="'.$gegevens['opdracht'].'" />
<input class="ins_punt" id="punt" name="punt['.$opdracht.']" type="text" value="'.$gegevens['punt'].'" />

the $opdracht is a php variable and consists of a number. The reason why it’s an array because the above code is iterated in a foreach loop and $opdracht is in every iteration a different value.

I’m expecting to receive 2 arrays: opdracht_titel and punt, with each one, as many values as iterations, and with an arraykey = $opdracht.

in the JS part of the AJAX I do:


var titel = document.getElementsByName("opdracht_titel[]");
var punt = document.getElementsByName("punt[]");

And pass titel and punt to a background script in php. (with an xmlhttpRequest => POST)
However the closest I get (besides errors, undefined, empty array, …) when doing a var_dump in the receiving php script is:


array(2) { ["titel"]=>  string(23) "[object HTMLCollection]" ["punt"]=>  string(23) "[object HTMLCollection]" }

which is IMHO a step in the right direction, but I must be missing something, somewhere…

Can anyone shed a light onto this one?

thx.

getElementsByName gives you an html collection, which is an array-like object. You would need to specify the first item from that collection to get the actual input, and then to ge the value of that input field.

For example:


var titel = document.getElementsByName("opdracht_titel[]")[0].value;

However, I would prefer to access the form fields via the form instead.


var form = document.getElementById('jobapplication'),
    titel = form.elements['opdracht_titel[]'].value;

However, you are using an ajax process to send the form data to the php script, so you can do that without needing to deal with any of the form fields.

For example:


function success(data) {
   // do stuff with data from php
   alert(data);
};

$.post("test.php", $("#jobapplication").serialize(), success);

Hi Paul,

Thx for your time.

I immediately tried your suggestions.

First option
Gives me following error in Firebug:
document.getElementsByName(“opdracht_titel”)[0] is undefined
http://localhost:8080/includes/js/copy/ajax1352808166.js
Line 160

2nd option
Same error
form.elements[‘opdracht_titel’] is undefined

Note: I changed my html code to:

<form name="koen" id="koen">

As you declared a var from with getElementById(‘koen’), …

3rd Option
If I understand correctly that is javascript code to process data FROM php? I need to get the html array into javascript (AJAX) and posted to PHP.

UPDATE:


var titel = document.getElementById("opdracht_titel").value;
var type = document.getElementById("type").value;
var punt = document.getElementById("punt").value;
postvars="rapport="+rapport+"&klas="+klas+"&titel[]="+titel+"&type[]="+type+"&punt[]="+punt;	

postvars is a variable that is used for the xmlhttp request and are basically the variables to sent to the php script.

previously I did not add the is that string. Which resulted in not having an array php side. Adding the results now in:


array(5) { ["rapport"]=>  string(1) "1" ["klas"]=>  string(2) "32" [B]["titel"]=>  array(1) { [0]=>  string(22) "Schrijven: Wie ben ik?" } ["type"]=>  array(1) { [0]=>  string(1) "2" } ["punt"]=>  array(1) { [0]=>  string(2) "10" }[/B] } 

Mind the 3 arrays nested in the $_POST array. The above code however is only sending the first array element… Think gonna rework now with info from Paul.

Bottom line: on the right track, but have not passed the finish (yet)

To whom it may concern:
The usual FORM-element array with <input>'s named “xyz[someindexname]” cannot be used when passing them to javascript.

Why? Javascript itself creates automatically an array from <input> fields that have the same name. So you just call them “xyz”. Or javascript chokes on the . The use of associative array indexes (HTML-level) is however ruled out by this behaviour.

Don’t know if this is exactly the truth, but it’s what I have found out these days.

So in my case how have I dealt with it:
Added a hidden input field which contains $opdracht value. So that array will store the associative index keys.

<form id="editrapport" name="editrapport">
									<fieldset class="editrapport">
										<legend>Bewerk rapport:</legend>
										<table class="editrapport">
										  <tbody><tr>
										    <!-- <th>Volgorde:</th> -->
										    <th>Type:</th>
										    <th>Nr:</th>
										    <th>Titel:</th>
										    <th>Punt:</th>
										  </tr><tr><td class="types">
						<select id="type" name="type" class="ins_type">
							<option value="1">
								Kleine overhoring (KO)
						   </option><option value="4">
								Leesoefening (LS)
						   </option><option value="3">
								Synthese proef (SP)
						   </option><option selected="" value="2">
								Taak (TK)
						   </option>
						</select>
					</td>
					<td class="nummer">
						1
					</td>
					<td class="opdrachten">
						<input type="text" value="Schrijven: Wie ben ik?" onkeyup="SearchSuggestions(32,1,this.value);" name="opdracht_titel" id="opdracht_titel" class="ins_opd">
						<div style="display: none;" id="autosug"></div>
					</td>
					<td class="punten">
						<input type="text" value="10" name="punt" id="punt" class="ins_punt">
					</td>
					<input type="hidden" value="1" name="opdracht" id="opdracht">					
				</tr><tr><td class="types">
						<select id="type" name="type" class="ins_type">
							<option selected="" value="1">
								Kleine overhoring (KO)
						   </option><option value="4">
								Leesoefening (LS)
						   </option><option value="3">
								Synthese proef (SP)
						   </option><option value="2">
								Taak (TK)
						   </option>
						</select>
					</td>
					<td class="nummer">
						1
					</td>
					<td class="opdrachten">
						<input type="text" value="Schooltaalwoorden" onkeyup="SearchSuggestions(32,1,this.value);" name="opdracht_titel" id="opdracht_titel" class="ins_opd">
						<div style="display: none;" id="autosug"></div>
					</td>
					<td class="punten">
						<input type="text" value="10" name="punt" id="punt" class="ins_punt">
					</td>
					<input type="hidden" value="2" name="opdracht" id="opdracht">					
				</tr><tr><td class="types">
						<select id="type" name="type" class="ins_type">
							<option value="1">
								Kleine overhoring (KO)
						   </option><option selected="" value="4">
								Leesoefening (LS)
						   </option><option value="3">
								Synthese proef (SP)
						   </option><option value="2">
								Taak (TK)
						   </option>
						</select>
					</td>
					<td class="nummer">
						1
					</td>
					<td class="opdrachten">
						<input type="text" value="Nieuwsbegrip" onkeyup="SearchSuggestions(32,1,this.value);" name="opdracht_titel" id="opdracht_titel" class="ins_opd">
						<div style="display: none;" id="autosug"></div>
					</td>
					<td class="punten">
						<input type="text" value="15" name="punt" id="punt" class="ins_punt">
					</td>
					<input type="hidden" value="3" name="opdracht" id="opdracht">					
				</tr></tbody></table>
				</fieldset>
				<fieldset class="submit">
					<button onclick="SaveChangesRapport(32,1);" name="submit" type="button" class="forms">
						<img src="/pics/lco_save.png">
						Opslaan
					</button>
					<button name="reset" type="reset" class="forms">
						<img src="/pics/lco_reload.png">
						Herstellen
					</button>
					<button onclick="GetRapport(32,1);" name="cancel" type="button" class="forms">
						<img src="/pics/lco_nokay.png">
						Annuleren
					</button>
				</fieldset>
			</form>

Notice the reoccuring sequence of <input> fields with the same name property. (background info: the form is dynamic so based on database content this can imply more sequences of the same fields, or less)

Then in javascript I do:

var form = document.getElementById('editrapport');
var posttitel="";
var posttype="";
var postpunt="";
var postopdracht="";
for(i=0;i < form.elements['opdracht_titel'].length;i++){
	posttitel = posttitel+"&titel[]="+form.elements['opdracht_titel'][i].value;
}
for(i=0;i < form.elements['type'].length;i++){
	posttype = posttype+"&type[]="+form.elements['type'][i].value;
}
for(i=0;i < form.elements['punt'].length;i++){
	postpunt = postpunt+"&punt[]="+form.elements['punt'][i].value;
}
for(i=0;i < form.elements['opdracht'].length;i++){
	postopdracht = postopdracht+"&opdracht[]="+form.elements['opdracht'][i].value;
}	
postvars="rapport="+rapport+"&klas="+klas+posttitel+posttype+postpunt+postopdracht;

and post it with xmlhttp resulting on php side in:

array(6) { ["rapport"]=>  string(1) "1" ["klas"]=>  string(2) "32" ["titel"]=>  array(3) { [0]=>  string(22) "Schrijven: Wie ben ik?" [1]=>  string(17) "Schooltaalwoorden" [2]=>  string(12) "Nieuwsbegrip" } ["type"]=>  array(3) { [0]=>  string(1) "2" [1]=>  string(1) "1" [2]=>  string(1) "4" } ["punt"]=>  array(3) { [0]=>  string(2) "10" [1]=>  string(2) "10" [2]=>  string(2) "15" } ["opdracht"]=>  array(3) { [0]=>  string(1) "1" [1]=>  string(1) "2" [2]=>  string(1) "3" } }

Which is as expected.

I’m still open for suggestions on how to do it better, but this seems to work.

me again…

Above code works perfectly IF there are multiple rows on the form. However when code is called with only one row, thus not providing an array to the JS function it fails (i.e. there are no arrays passed to the php code)

Unfortunately there seems no such thing as is_array() in javascript as I know from PHP. Also Array.isArray seems to give me an alert that “no such function exist” alltough Google seems to be fairly confident in it’s existence…

Is there an “easy way” to check if an object is an array (applicable to the JS code in my previous post)?

Changed my code to:

	if(typeof form.elements['opdracht_titel'].value == "string"){
		posttitel = "&titel="+document.getElementById("opdracht_titel").value;
		posttype = "&type="+document.getElementById("type").value;
		postpunt = "&punt="+document.getElementById("punt").value;
		postopdracht = "&opdracht="+document.getElementById("opdracht").value;
	}
	else{
		for(i=0;i < form.elements['opdracht_titel'].length;i++){
			posttitel = posttitel+"&titel[]="+form.elements['opdracht_titel'][i].value;
		}
		for(i=0;i < form.elements['type'].length;i++){
			posttype = posttype+"&type[]="+form.elements['type'][i].value;
		}
		for(i=0;i < form.elements['punt'].length;i++){
			postpunt = postpunt+"&punt[]="+form.elements['punt'][i].value;
		}
		for(i=0;i < form.elements['opdracht'].length;i++){
			postopdracht = postopdracht+"&opdracht[]="+form.elements['opdracht'][i].value;
		}
	}

Is this a good alternative? typeof returns “undefined” when submitting an array. Don’t know if using typeof this way might cause other issues…

BTW: it seems to work.

Why are you manually creating the URL? jQuery has the serialize() method that automatically does all of that hard work for you.


function success(data) {
   // do stuff with data from php
   alert(data);
};
 
$.post("test.php", $("#jobapplication").serialize(), success);

The last line posts an ajax request to the php page, and the success function handles any reponse from the php page. The success part is completely optional though.

That’s because I don’t use jQuery, just plain Javascript.

Ahh, then let’s take care of things in a more flexible and less labour intensive manner.

You’re going to want to first serialize the form elements in to an object, and to then use JSON.stringify(serialized) to turn that object in to data that AJAX can easily send.


var form = document.getElementById('editrapport'),
    serialized = serializeForm(form.elements),
    stringified = JSON.stringify(serialized);

Serializing the form is quite easy.


function serializeForm(obj) {
    var serialized = {},
        prop;
    for (prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            serialized = serializeField(obj[prop], serialized);
        }
    }
    return serialized;
}

When serializing a field, we want to use the field name as the key.

If the object already has a property of that name, and it isn’t already an array, we can convert it in to one before adding the new value to it.


function serializeField(field, target) {
    var value = field.value;
    if (!value) {
        return target;
    }
    if (field.name) {
        if (target.hasOwnProperty(field.name)) {
            if (!Array.isArray(target[field.name])) {
                target[field.name] = [target[field.name]];
            }
            target[field.name].push(value);
        } else {
            target[field.name] = value;
        }
    }
    return target;
}

We can deal with compatibility issues of older web browsers by polyfilling in the isArray method if it doesn’t already exist.


if (!Array.isArray) {
    Array.isArray = function (vArg) {
        return Object.prototype.toString.call(vArg) === "[object Array]";
    };
}

The following shows an example of the above in action: http://jsfiddle.net/pmw57/aphJd/