Come usare l'oggetto XMLHttpRequest
Per iniziare abbiamo bisogno di uno script server-side a cui fare la richiesta. Lo script getCityAndState.asp cerca in una tabella del database un dato ZIP code (ndr.: Codice di Avviamento Postale), passato in una query string o come il campo di una form, per restutuire la città e lo stato a cui è associato.
Lo script restituisce questa informazione come un unica riga di testo nel formato city_name,state_abbr (nel caso dell'esempio qui sotto: Las Vegas,NV). Per testare quanto detto basti provare il link qui sotto, dove lo ZIP code è passato nella querystring:
getCityAndState.asp?zipCode=89120
Per il nostro primo esempio usiamo una form con un campo per ZIP code, città e stato come la seguente:
<form action="/common/formdump.asp" method="post">
<div>
ZIP Code: <input name="zipCode" type="text" size="5" maxlength="5"
onchange="initiateCityStateLookup(event);" />
City: <input name="city" type="text" size="20" />
State: <input name="state" type="text" size="2" /><br />
<input name="submit" type="submit" value="Submit" />
<input name="reset" type="reset" value="Clear" />
</div>
</form>
Osservate che il campo ZIP code ha il proprio evento onchange settato per chiamare la funzione initiateCityStateLookup(). L'idea è quella di completare in maniera automatica i campi città e stato quando l'utente inserisce uno ZIP code. Aggiungiamo quindi il codice JavaScript:
var cityStateLookup = getXMLHttpRequest();
function initiateCityStateLookup(event)
{
// Get the zip code.
var zipCode = document.forms[0].elements["zipCode"].value;
// Make a request to get the matching city and state.
var url = "getCityAndState.asp?zipCode=" + zipCode;
cityStateLookup.open("GET", url, false);
cityStateLookup.send(null);
// Fill in the city and state fields, if available.
try
{
var data = cityStateLookup.responseText.split(",");
if (data.length == 2)
{
document.forms[0].elements["city"].value = data[0];
document.forms[0].elements["state"].value = data[1];
}
else
alert("ZIP code not found.");
}
catch (ex)
{}
}
Definiamo innanzitutto la variabile cityStateLookup come istanza dell'oggetto XMLHttpRequest usando la funzione descritta prima. Quando l'utente inserisce un valore nel campo ZIP code e toglie il focus da quel campo avviene l'evento onchange,
che chiama initiateCityStateLookup().
La funziona invocata prende lo ZIP code inserito dall'utente e costruisce l'URL per fare la richiesta HTTP, usando lo ZIP code come parametro query string.
Va notato a questo punto che, per questioni di sicurezza, l'oggetto XMLHttpRequest può solamente chiamare URL all'interno del proprio stesso dominio.*
Per esemio, una pagina caricata da http://www.example.net/sample/client.html non può utilizzare XMLHttRequest dati provenienti da http://www.test.com/scripts/server.php.
In più, se la pagina fosse anche accessibile da http://example.net/sample/client.html (senza il www), l'oggetto XMLHttpRequest dovrebbe usare example.net invece di www.example.net quando vien formulata la richiesta, questo perchè il controllo del dominio è molto stretto. Per fotuna si possono usare URL relativi, tipo /sample/server.php per aggirare il problema.
*In IE, esiste la possibilità di accedere a URL su un dominio diverso in base ai settaggi individuali del browser (ricordiamo che si tratta di un oggetto ActiveX in questo caso). Pur essendo permesso, il browser mostrerà un pop-up con un messaggio di avvertimento, informando l'utente sui rischi inerenti e chiedendo l'autorizzazione a procedere.Il metodo open() è chiamato per inizializzare la richiesta. Notate che il terzo parametro è settato a false, ossia che la richiesta invocata non è di tipo asincrono. In altre parole il browser deve attendere che la richiesta sia completata prima di procedere con l'esecuzione della riga successiva di codice JavaScript.
Alla ricezione dellla risposta, i dati vengono scritti nella proprietà responseTextdell'oggetto XMLHttpRequest. Questa stringa di testo può essere parsata per ottenere città e stato corrispondenti allo ZIP code inserito dall'utente. QUesti dati vengono successivamente copiati dalla funzione negli appositi campi. Se non viene restituito nessun dato, i campi rimangono invariati e viene mostrato un messaggio di errore.
Se volete fare una prova andate a questa pagina, l'esempio qui riportato non funziona per il motivo descritto nel riquadro (provandolo viene restituito Error: Invalid referer, che ci dimostra l'impossibilità di effettuare la chiamata da un dominio diverso da quello sul quale lo script server-side risiede).
C'è un potenziale problema con questo codice, ossia che il browser deve aspettare la risposta dal server. Nel caso di un lungo delay, nell'attesa che la funzione send()completi la chiamata, l'interfaccia utente rimane bloccata, rendendo la pagina non molto user-friendly.
Come fare una richiesta asincrona
Per evitare che questo possa succedere va specificato true come terzo parametro quando si invoca la funzione open().
Questo farà tornare la funzione send() immediatamente in seguito alla sua chiamata, in questo modo il browser non dovrà fermare l'esecuzione del codice mentre attende la risposta del server.
La domanda ora è: come facciamo a sapere quando la richiesta viene completata? L'oggetto XMLHttpRequest fornisce una proprietàdi sola lettura chiamata
readyState che riflette l'avanzamento della richiesta. L'oggetto fornisce inoltre un metodo che si chiama onreadystatechangeche, come suggerisce il nome, viene invocato quando la proprietà readyState cambia valore.
readyState è un numero intero compreso tra zero e quattro. Il significato di ogni stato è descritto di seguito:
- 0 - UNINITIALIZED,
open()non è stata ancora chiamato. - 1 - LOADING,
open()è stato chiamato masend()non ancora. - 2 - LOADED,
send()è stato chimato e il response status e gli header sono stati ricevuti. - 3 - INTERACTIVE, i dati di risposta sono in fase di download.
- 4 - COMPLETE, la richiesta è completa e tutti i dati sono stati scaricati.
La maggior parte delle volte sarete interessati unicamente allo stato COMPLETE e ignorerete gli altri quattro stati. Nell'esempio precedente possiamo definire un handler dell'evento
onreadystate in questo modo:
function cityStateReadyStateChange()
{
// Check the ready state.
if (cityStateLookup.readyState == XMLHTTPREQUEST_READY_STATE_COMPLETED)
{
// Fill in the city and state fields, if available.
try
{
var data = cityStateLookup.responseText.split(",");
if (data.length == 2)
{
document.forms[0].elements["city"].value = data[0];
document.forms[0].elements["state"].value = data[1];
}
else
alert("ZIP Code not found");
}
catch (ex)
{}
}
}
Utilizziamo qui la costante
XMLHTTPREQUEST_READY_STATE_COMPLETED definita in precedenda, invece dell'hardcoding del numero 4.
Possiamo ora modificare il codice di initiateCityStateLookup() per assegnare l'handler dell'evento come segue:
function initiateCityStateLookup(event)
{
// Get the zip code.
var zipCode = document.forms[0].elements["zipCode"].value;
// Perform an asynchronous request to get the matching city and state.
var url = "getCityAndState.asp?zipCode=" + zipCode;
cityStateLookup.onreadystatechange = cityStateReadyStateChange;
cityStateLookup.open("GET", url, true);
cityStateLookup.send(null);
}
Questa demo è il riassunto di quanto visto finora, con l'eccezione che aggiunge la validazione dello ZIP code prima di fare la richiesta e mostra un messaggio sulla paigna per ogni stato in qui la proprietà readyState viene a trovarsi.
Una cosa alla quale non abbiamo prestato ancora attenzione sono le proprietà status e
statusText, che sono disponibili quando lo stato ready raggiunge il valore LOADED. La proprietà status contiene lo status code HTTP tornato dal server mentre statusText è un breve testo che descrive lo status code. Una richiesta con esito positivo avrà uno status code 200, ossia "OK". Altri status code comuni sono 404, ossia "Not Found", oppure 500, "Server Error". Queste proprierà possono essere usate per determinare se il server ha prodotto dei dati validi oppure per fare il debugging.
function myReadyStateChange()
{
if (myXMLHttpRequest.readyState == XMLHTTPREQUEST_READY_STATE_COMPLETED)
{
if (myXMLHttpRequest.status == 200)
{
// Process the data.
...
}
else
{
alert("Error: HTTP "
+ myXMLHttpRequest.status
+ " "
+ myXMLHttpRequest.statusText);
}
}
}
Fino ad ora abbiamo visto come effettuare una chiamata asincrona e la parte inerente al JavaScript di AJAX. Nella prossima pagina guarderemo la parte XML.