Using the HttpRequest Object
[Guarda la pagina HttpRequest demo per vedere la versione completa del codice]
Per questa demo combineremo le funzionalità dei due esempi precedenti. Gli utenti potranno cercare lo ZIP code partendo da città e stato inseriti e vice versa.
Nel codice useremo un unico oggetto HttpRequest per gestire entrambe le richieste. Come prima cosa settiamo un request header "Content-Type" in modo da poter postare i dati rilevanti.
var postRequest = new HttpRequest();
postRequest.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
postRequest.failureCallback = requestFailed;
Settiamo inoltre una funzione di callback per mostrare l'errore all'utente nel caso in cui qualche richiesta debba fallire.
function requestFailed(httpRequest)
{
setStatusText("Lookup failed, HTTP " + httpRequest.status
+ " " + httpRequest.statusText + ".");
}
L'utente inizia la ricerca cliccando do uno dei due link presenti sulla pagina. Il primo cercherà in una lista di ZIP code relativi alla città e allo stato inseriti dall'utente. Questa richiesta avviene tramite una chiamata a getZipCodes(). Il codice è fondamentalmente quello di prima, eccetto che ora usiamo l'oggetto HttpRequest da noi definito invece che l'oggetto XMLHttpRequest.
function getZipCodes(event)
{
// Clear the status text.
setStatusText("");
// Check for a valid 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;
}
// Reset the zip code drop down list.
resetZipCodeList();
// Encode the data to be POSTed.
var city = encodeURI(city);
var state = encodeURI(state);
// Perform an asynchronous request to get a list of zip codes for that
// city and state.
postRequest.url = "getZipCodes.asp";
postRequest.successCallback = zipCodeCallback;
postRequest.post("city=" + city + "&state=" + state);
}
Notare che basta settare l'URL e la funzione di callback per far funzionare il tutto. Il metodo
post()dell'oggetto gestisce l'apertura della connessione, il settaggio degli header e l'invio dei dati.
In caso la richiesta venga portata a termine con successo la funzione zipCodeCallback()viene chiamata per
elaborare i dati restituiti. Anche in questo caso la funzione è simile a quella precedentemente usata, con la differenza che in questo caso non abbiamo bisogno di controllare
ready state o status codes; questo perchè sappiamo già che la risposta è valida, dobbiamo solo controllare i dati restituiti.
function zipCodeCallback(httpRequest)
{
// Assume no matches were found.
statusText = "No ZIP Codes found."
// Set focus back on the city field.
document.forms[0].elements["city"].focus();
// Get the XML document returned from the request and fill in the form
// fields.
try
{
var xmlDoc = httpRequest.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;
}
// Get all the zip code tags returned from the request.
var els = xmlDoc.getElementsByTagName("zipCode");
// Set the status text.
statusText = els.length + " ZIP Codes found."
// 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);
}
// Save the current zip code.
var zipCode = document.forms[0].elements["zipCode"].value;
var found = false;
// 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++)
{
// Create the option.
option = document.createElement("OPTION");
option.text = option.value = els[i].firstChild.nodeValue;
// Is the current zip code in the list?
if (option.text == zipCode)
found = true;
// Add the option to the list.
try
{
document.forms[0].elements["zipCodeList"].add(option, null);
}
catch(ex)
{
// For IE.
document.forms[0].elements["zipCodeList"].add(option);
}
}
// If the current zip code is not in the list, clear it.
if (!found)
document.forms[0].elements["zipCode"].value = "";
// Show the drop down list and set focus on it.
document.forms[0].elements["zipCodeList"].style.visibility = "";
document.forms[0].elements["zipCodeList"].focus();
}
catch (ex)
{}
// Display the status message.
setStatusText(statusText);
}
L'altro link funziona in modo simile, effettua una chiamata alla funzione
getCityState()per la quale dobbiamo solo definire un URL diverso e una funzione di callback diversa.
function getCityState(event)
{
// Clear the status text.
setStatusText("");
// Check for a valid zip code.
var zipCode = document.forms[0].elements["zipCode"].value;
if (zipCode == "")
{
setStatusText("Enter a ZIP Code.");
return;
}
if (!zipCode.match(zipCodeFormat))
{
setStatusText("Invalid ZIP Code.");
return;
}
// Reset the zip code drop down list.
resetZipCodeList();
// Perform an asynchronous request to get the matching city
// and state.
postRequest.url = "getCityAndState.asp";
postRequest.successCallback = cityStateCallback;
postRequest.post("zipCode=" + zipCode);
}
Anche in questo caso la funzione di callback è semplificata perche deve solo elaborare i dati restituiti.
function cityStateCallback(httpRequest)
{
// Assume no match was found.
var statusText = "ZIP Code not found."
// Fill in the city and state fields, if available.
try
{
var data = httpRequest.responseText.split(",");
if (data.length == 2)
{
document.forms[0].elements["city"].value = data[0];
document.forms[0].elements["state"].value = data[1];
statusText = "ZIP Code found."
}
}
catch (ex)
{}
// Update the status message.
setStatusText(statusText);
}
In questa demo utilizziamo un unico oggetto HttpRequest per gestire entrambe le funzioni di ricerca. Avremmo anche potuto utilizzare due oggetti distinti e consentire così un'esecuzione simultanea, ma siccome le due funzioni chiamate sono in qualche modo in contraddizione tra loro, è meglio usarne sono uno. Siccome l'oggetto HttpRequest termina automaticamente la richiesta attiva prima di iniziarne una nuova, usando un sono oggetto non dobbiamo preoccuparci delle eventuali interferenze che due oggetti distinti potrebbero invece causarsi a vicenda. In altre circostanze comunque potrebbe essere accettabile fare richieste simultanee utilizzando più istanze dello stesso oggetto.
Conclusioni
AJAX è un'estensione della programmazione DHTML, che aggiunge la possibilità di spedire e ricevere dati da un web server in risposta alle azioni dell'utente.
L'oggetto XmlHttpRequest fornisce tutte queste funzionalità. Sebbene il suo utilizzo possa non essere intuitivo, abbiamo mostrato come si può estendere XmlHttpRequest utilizzando un oggetto JavaScript user-defined per celare le difficoltà di effettuare richieste asincrone, gestire gli errori, e ottenere i dati in risposta. Il risultato finale è un'interfaccia relativamente semplice che permette di concentrarsi su ciò che si sta facendo invece che su come farlo.