Using XML
[Guarda la pagina XML demo per vedere la versione completa del codice]
Nell'esempio precedente, il server ha risposto con una stringa di testo semplice alla richiesta. Ma cosa succede se bisogna gestire più dati? Oppure dati strutturati? L'oggetto XMLHttpRequest automaticamente può fare il parsing di documenti XML mandati in risposta a una sua richiesta.
Ovviamente prima abbiamo bisogno di uno script server-side che faccia l'output di un documento XML. getZipCodes.asp fa al caso nostro: assomiglia allo script usato nell'esempio precedente, prende come parametri città e stato e ritorna un insieme di ZIP code in formato XML. Si può testare lo script cliccando sul link sottostante:
getZipCodes.asp?city=las+vegas&state=nv
Per il nostro prossimo esempio, utilizzeremo questi dati per popolare una select (menu a tendina) in seguito all'inserimento di città e stato da parte dell'utente. Per provare la demo, inserisci una città degli stati uniti e il codice relativo al suo stato (ndr. prova con new york, ny) e poi clicca su "Lookup Zip Codes". Una volta che la lista degli ZIP code viene caricata, se ne può selezionare uno dal menu e questo viene copiato nel campo ZIP code.
Dando un'occhiata al codice notiamo che la richiesta inizia in modo analogo all'esempio precedente. Prima di effettuare la richiesta viene validata la form, assicurandosi che città e stato siano stati entrambi inseriti, che venga cancellato il valore eventualmente presente nel campo ZIP code e tutte le entry presenti nel menu a tendina, prima che questo venga nascosto (hidden) all'utente.
var zipCodeLookup = getXMLHttpRequest();
function initiateZipCodeLookup(event)
{
// Clear the status text.
setStatusText("");
// Check for a city and state.
var city = document.forms[0].elements["city"].value;
var state = document.forms[0].elements["state"].value;
if (city.length == 0 || state.length != 2)
{
setStatusText("Enter a city and two-letter state abbrev.");
return;
}
// Clear the current zip code.
document.forms[0].elements["zipCode"].value = "";
// Hide the drop down list.
document.forms[0].elements["zipCodeList"].style.visibility = "hidden";
// Clear the zip code drop-down list.
while (document.forms[0].elements["zipCodeList"].options.length > 0)
document.forms[0].elements["zipCodeList"].remove(0);
// Abort any currently active request.
zipCodeLookup.abort();
// Perform an asynchronous request to get a list of zip codes for
// that city and state.
var url = "getZipCodes.asp?city=" + encodeURI(city)
+ "&state=" + encodeURI(state);
zipCodeLookup.onreadystatechange = zipCodeReadyStateChange;
zipCodeLookup.open("GET", url, true);
zipCodeLookup.send(null);
}
Notare l'uso della funzione built-in encodeURI() applicata ai valori forniti dai campi della form. Questa funziona garantisce che nella query string che spediamo al server non ci siano caratteri non validi.
Anche la funzione che si occupa dell'evento onreadystatechange è simile alla precedente. La differenza sta nel come i dati ritornati vengono processati. Il documento XML ha questa forma:
<zipCodes city="Las Vegas" state="NV"> <zipCode>89101</zipCode> <zipCode>89102</zipCode> <zipCode>89103</zipCode> <zipCode>89104</zipCode> ... </zipCodes>
La proprietà responseXML dell'oggetto XMLHttpRequest è un documento DOM. E' accessibile in tutti i suoi noti, attributi e valori attraverso codice JavaScript, esattamente nello stesso modo in cui si accede al DOM di una pagina web.
Nell'XML mostrato sopra zipCodeLookup.responseXML.documentElement
è il nodo relativo al tag <zipCodes> mentre zipCodeLookup.responseXML.getElementsByTagName("zipCode") restituisce l'array di nodi che rappresentano i tag <zipCode>.
In questa demo, il codiche che si occupa di processare l'XML ritornato dal server come prima cosa quarda al nodo root per estrarre i valori degli attributi
city e state:
var xmlDoc = zipCodeLookup.responseXML;
// Copy the city and state attributes from the root XML node to the
// appropriate form fields.
var city = xmlDoc.documentElement.getAttribute("city");
var state = xmlDoc.documentElement.getAttribute("state");
if (city.length > 0 && state.length > 0)
{
document.forms[0].elements["city"].value = city;
document.forms[0].elements["state"].value = state;
// Clear the current zip code.
document.forms[0].elements["zipCode"].value = "";
}
Questi sono utilizzati per aggiornare i campi corrisponenti nella form sulla pagina. Questa operazione potrebbe sembrare inutile, in quanto l'utente ha appena inserito città e stato, ma ci assicura che maiuscole e minuscole siano messe in modo giusto ed è un buon esempio di come estrarre questi dati dall'XML.
Fatto ciò lo script fa un loop dei tag <zipCode> nel codice XML per estrarre la lista di ZIP code.
// Get all the zip code tags returned from the request.
var els = xmlDoc.getElementsByTagName("zipCode");
// Add a dummy option to the zip code drop-down list.
var option = document.createElement("OPTION");
option.text = "Select one...";
option.value = "";
try
{
document.forms[0].elements["zipCodeList"].add(option, null);
}
catch(ex)
{
// For IE.
document.forms[0].elements["zipCodeList"].add(option);
}
// Add an option to to the drop-down list for each zip code
// returned from the request.
for (var i = 0; i < els.length; i++)
{
option = document.createElement("OPTION");
option.text = option.value = els[i].firstChild.nodeValue;
try
{
document.forms[0].elements["zipCodeList"].add(option, null);
}
catch(ex)
{
// For IE.
document.forms[0].elements["zipCodeList"].add(option);
}
}
// Show the drop down list and set focus on it.
document.forms[0].elements["zipCodeList"].style.visibility = "";
document.forms[0].elements["zipCodeList"].focus();
Per ogni nodo lo script estrae il testo e crea un nuovo elemento OPTION che viene aggiunto alla SELECT, che viene di seguito resa visibile all'utente.
La SELECT ha un handler dell'evento onchange assegnato. Questo handler copia l'item selezionato nel campo ZIP code.
function setZipCode(event)
{
// Copy the zip code selected from the drop-down list to the text
// zip code field.
var n = document.forms[0].elements["zipCodeList"].selectedIndex;
document.forms[0].elements["zipCode"].value =
document.forms[0].elements["zipCodeList"].options[n].value;
// Clear the current selection.
document.forms[0].elements["zipCodeList"].selectedIndex = -1;
}
Fare il POST dei dati in una richiesta HTTP
[Guarda la pagina POST demo per vedere la versione completa del codice]Entrabe le precedenti demo usavano una query string per passare i dati allo script server-side. Si può anche passare i dati usando il metodo POST, ossia il metodo usato tipicamente in una form HTML.
Questa demo fa la setessa cosa dell'ultima vista, con l'eccezione che usa il metodo POST per passare i dati al web server. L'unica differenza nel codice sta nella funzione initiateZipCodeLookup():
// Abort any currently active request.
zipCodeLookup.abort();
// Encode the data to be POSTed.
var city = encodeURI(document.forms[0].elements["city"].value);
var state = encodeURI(document.forms[0].elements["state"].value);
// Make the call to get a list of zip codes for that city and state.
var url = "getZipCodes.asp"
// Perform an asynchronous request to get a list of zip codes for that
// city and state.
var url = "getZipCodes.asp";
zipCodeLookup.onreadystatechange = zipCodeReadyStateChange;
zipCodeLookup.open("POST", url, true);
zipCodeLookup.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
zipCodeLookup.send("city=" + city + "&state=" + state);
Notare che non c'è alcuna query string appesa all'URL, che la chiamata al metodo
open() usa "POST" invece di "GET" e che la chiamata
send() include una stringa di dati.
Questa stringa ha lo stesso formato della query string usata in precedenza, ossia name1=value1&name2=value2.... Nella richiesta al server va aggiunto anche l'header "Content-Type" col valore "application/x-www-form-urlencoded" che dichiara al server che formato aspettarsi. Non è necessario invece aggiungere esplicitamente l'header "Content-Length", perchè l'oggetto XMLHttpRequest calcola automaticamente questo valore.
Importante: prima di chiamarle la funzione setRequestHeader() va chiamato il metodo open(),
altrimenti si verifica un errore.
Si possono utilizzare altri formati oltre all'XML. Tutto dipende da quali formati il script o il programma CGI che sta sul web server si aspettano.
Di seguito guarderemo a un po' di codice che dovrebbe rendere più semplice effettuare richieste e processare risposte, in modo tale che ci si possa concentrare sul progetto più che sul far funzionare la tecnologia.