lunedì 4 febbraio 2008

Ajax: da dove inizio?

Che cosa e' Ajax?

Ecco la prima domanda che ci viene in mente quando leggiamo per la prima volta qualche articolo sulla programmazione web che ci parla di questo nuovo Ajax!Magari siamo abituati a fare le nostre pagine web dinamiche in php, in jsp oppure in asp.
E leggiamo che se le facessimo in Ajax potremmo anche fare quell'applicazione ultra-avanzata che non abbiamo idea come siano riusciti a farla cosi' veloce e cosi'...nuova rispetto alle nostre pagine web!

Vi siete magari chiesti come hanno fatto a fare le mappe di Google, Gmail oppure il sito di Flickr, o se siete piu' curiosi come hanno potuto creare una
pagina che sembra un foglio excel, e soprattutto funziona come un foglio excel, con anche il copia-incolla!!!!

Sicuramente si trovano migliaia di siti ormai che ci spiegano cosa e' Ajax, che non e' un linguaggio vero e proprio, che e' un acronimo e da quali parole e' formato.

Ok, d'accordo, ma in pratica? Come si inizia con Ajax???

Ecco dove casca l'asino....come si dice in questi casi.
Qui su questa domanda ci sono molte opinioni in proposito, non tutte concordanti tra loro.
Chi vi suggerisce un framework, chi un altro.
Chi vi spiega come si usa l'oggetto XMLHttpRequest, chi vi dice "no attenzione, su IE6 devi usare l'ActiveX!".
Chi vi racconta l'uso di una libreria integrata con php, che di un'altra integrata con jsp oppure l'utilizzo di AJAX.NET.

Supponiamo pero' di essere nei panni dello sviluppatore di pagine web "old style" medio, che conosce abbastanza bene l'html, sa usare pure i css e un po' di javascript per validare i campi di un form. E che conosce php, oppure jsp, oppure asp, oppure ASP.NET ma non vuole legarsi necessariamente a nessuno di essi.

Beh il primo punto di partenza per iniziare con Ajax e' migliorare la nostra conoscenza con Javascript!

Mentre prima scrivevamo 10 righe di javascript nelle nostre pagine web, ora ci tocchera' scriverne almeno 20 volte tanto! E dovremo pure essere capaci a manipolare il Document Object Model della nostra pagina web.

Non sai cosa e' il D.O.M.? Prima di proseguire oltre leggi bene
qui ,qui e qui.

E dopo Javascript? Mi serve altro oppure posso gia' iniziare con Ajax?

In teoria e' sufficiente, pero' come diceva una famosa pubblicita' la potenza e' nulla senza controllo. Javascript ci fornisce la potenza necessaria. Ma sapere cosa e' ajax nel profondo ci fornisce il controllo che ci manca.
Mi serve sapere qualcosa di piu' su come usare javascript per avere una pagina Ajax. Ovvero capire in cosa e' diversa una pagina ajax rispetto ad una pagina web "normale", e perche' mai una pagina ajax e' "meglio" di quella normale.

Ajax e' ne' piu' ne' meno che la capacita' modificare la mostra pagina potendo richiedere i nuovi dati dinamicamente al webserver senza usare il metodo submit() con il tag


<form action="destinazione.php" method="POSTGET"></form>

In piu' abbiamo la possibilita' di fare questa richiesta in modo a-sincrono. Ma su quest'aspetto torneremo piu' tardi.

Veniamo innanzitutto al tag form:

quando abbiamo bisogno di modificare dinamicamente la nostra pagina web con nuovi contenuti solitamente che facciamo? Richiamiamo la nostra pagina dinamica passando dei parametri per recuperare una versione alternativadella stessa pagina in cui qualche dato e' cambiato.
Se stiamo recuperando la lista dei CAP di una citta' in un combobox nella nostra pagina web, e digitiamo "Milano", ricarichiamo la nostra pagina con l'elenco giusto passando ad esempio come parametro "destinazione.php?city=milano". Fin qui nulla di sconvolgente.

Ma se potessimo chiedere al server di sarci solo la lista dei CAP senza tutto il resto? In fondo a noi serve quello, non tuttala pagina. Poi magari attacchiamo la lista dei CAP alla combobox che compare nella nostra pagina sul browser.
Con il tag form non possiamo, perche' il metodo submit() richiede tutta la pagina ricaricandola dal server. E facendo questo la vecchia pagina visualizzata sul browser viene sovrascritta completamente.

Per far cio' abbiamo a disposizione ora sui browser di ultima generazione (IE7, Firefox2, Opera9, Safari3) un metodo alternativo:il metodo send() presente in un oggetto javascript chiamato XMLHttpRequest.

Ma possiamo usarlo subito oppure occorre fare qualche operazione preliminare? Dobbiamo caricare qualcosa prima?

No, non dobbiamo caricare nulla, a meno di non usare IE6. In quel caso non si ha a disposizione quell'oggetto internamente nel browser,ma occorre caricare un oggetto ActiveX esterno. Vedremo dopo come si fa. Proseguiamo supponendo di averlo gia' a disposizione.

Quindi come si usa ora quel metodo send()?

Ecco, occorre un minimo di preparazione per poterlo usare. Perche' occorre sapere come dirgli cosa vogliamo caricare e capirecome utilizzare i risultati che ci verranno restituiti dal server una volta elaborata la nostra richiesta della lista dei CAP.

E qui interviene il javascript, poiche' l'unico modo per utilizzare il metodo send() di quell'oggetto e' tramite javascript:

Metteremo tutto il codice javascript in un file esterno alla nostra pagina web.
Dobbiamo anche entrare un un preciso ordine di idee: dobbiamo sempre pensare che il codice javascript viene eseguito, ed ha il suo ambito di efficacia, solo sul client. Quindi dobbiamo sempre ricordarci che chi effettua le richieste verso il web server remotoe' il browser su cui stiamo mostrando la nostra pagina web.

Quindi mentre prima quando programmavamo una pagina dinamica in php consideravamo tutto dal punto di vista del server su cui viene eseguito il codice php, ora ribaltiamo il nostro punto di osservazione poiche' il codice javascript viene eseguito sul client.

Vediamo il codice che ci serve per usarlo:


// se il browser e' != IE6 il typeof di XMLHttpRequest e' diverso da 'undefined'
// in questo modo si puo' capire se stiamo usando IE6 oppure no
// Con IE6 ridefiniamo la funzione XMLHttpRequest
// cosi' da caricare l'ActiveX alternativo per usare le chiamate Ajax
// anche quando chiameremo la funzione XMLHttpRequest()
if (typeof(XMLHttpRequest) == 'undefined')
var XMLHttpRequest = function() {
return new ActiveXObject('Microsoft.XMLHTTP');
}

// Questa e' la funzione principale per effettuare le chiamate ajax.
// occorre passare l'url che si vuole richiedere e la funzione di
// callback da chiamare quando si ricevono i risultati dal webserver
function sendRequest(url, callBack) {

// inizializziamo l'oggetto base delle chiamate ajax
// in questo modo, con l'if di sopra, abbiamo o la chiamata diretta
// alla funzione che carica l'oggetto XMLHttpRequest nativo
// oppure la chiamata alla funzione ridefinita che carica l'ActiveX
var req = new XMLHttpRequest();

// costruiamo l'eventhandler che collega la funzione di callback
// sull'evento onreadystatechange attivato dall'oggetto XMLHttpRequest
// ogni volta che cambia il suo stato interno a fronte della richiesta ajax
req.onreadystatechange = function(){

// readyState==4 quando la richiesta e' stata elaborata e si riceve il risultato dal server
if (req.readyState == 4 )){

// status==200 quando la risposta dal server e' stata ricevuta correttamente
if(req.status == 200){

// chiamo la funzione di callback passando l'oggetto XMLHttpRequest come riferimento
// per manipolare il risultato ricevuto memorizzato nelle proprieta'
// req.responseText oppure req.responseXML
callBack(req);
}else{
alert("errore");
}
}
}

// inizializziamo il canale con il server passando il tipo di richiesta (POST o GET)
// l'url o indirizzo del server cui fare la richiesta
// e true o false se vogliamo fare una richiesta asincrona o sincrona
req.open("POST", url, true);

// effettuiamo l'attivazione della richiesta remota
req.send();
}

Innanzitutto dobbiamo caricare l'oggetto associando una sua nuova istanza alla variabile req.

In questo modo viene caricata l'ActiveX nel caso in cui si sta usando IE6 dentro il primo blocco if(){}.

NOTA: Se l'oggetto e' incluso nel browser, il tipo associato e' diverso da 'undefined'. Quindi testando il typeof() sappiamo che l'oggetto non e' interno al browser.

Per cui in quel caso "ritorniamo", ovvero carichiamo, l'oggetto ActiveX alternativo.

Non controlliamo nemmeno se il caricamento va bene, perche' o l'oggetto e' interno oppure e' un ActiveX.Se va male l'ActiveX vuol dire che il nostro browser ha dei problemi gravi e non funziona correttamente, e ce ne saremmo gia' accorti.

Il metodo send() ha bisogno di sapere se la richiesta viene fatta in GET oppure in POST, come col tag form.Poi deve sapere l'url, ovvero quale risorsa richiedere al webserver - come nell'attributo action di prima. Infine serve dirgli se fare la richiesta in modo asincrona, percio' diciamo "true", cioe' "vero" per significare si.Tutti queste informazioni vengono fatte "aprendo" l'oggetto XMLHttpRequest con il metodo open();

Ok. Adesso dobbiamo capire come intercettare i dati quando ci vengono inviati dal server.
Mentre prima con form non dovevamo preoccuparci di fare nulla, poiche' ci pensava il browser a ricaricare la pagina ricevuta dal server, adesso dobbiamo essere noi ad intercettare i dati restituiti e a "piazzarli" al loro posto. Per capire quando e se i dati sono arrivati, ci vengono in soccorso due stati associati all'oggetto req in collegamento all'evento onreadystatechange:

  • l'evento notifica le varie fasi di ricezione dei dati remoti variando il valore di uno stato interno. Ci basta sapere a questo punto che i dati sono stati ricevuti dal server quando readyState==4.
  • a quel punto controlliamo l'attributo status che indica il codice ritornato dal server, che altri non e' che il noto codice di ritorno del protocollo HTTP. Ci basta sapere che se i dati sono ricevuti corretti il codice e' 200. Cio' vuol dire che in tutti gli altri casi i dati o sono stati ricevuti con un codice di errore oppure non sono ancorastati ricevuti. Per questo motivo mostriamo un alert di errore se sono stati ricevuti con stato !=200.


In teoria bisognerebbe gestire anche il caso in cui non si arrivi ad un readyState=4, ma per ora non ce ne occupiamo per semplicita'.

L'evento onreadystatechange non siamo noi che lo gestiamo, ma viene gestito in modo asincrono direttamente dal browser.Cioe' il nostro browser in un thread parallelo controlla il passaggio degli stati collegati a questo evento, facendolo scattareper avvisare , per cosi' dire, l'"utente" della pagina che ha fatto la richiesta che tale richiesta sta venendo processata in parallelo.

Cio' significa che la nostra pagina web non viene bloccata finche' non si ricevono tutti i dati, ma l'utente puo' continuare ad interagire con essa. Mentre prima con form tutto si bloccava finche' la pagina non veniva ricaricata completamente. Adesso che sappiamo come capire se i dati sono arrivati, come li sistemiamo al loro posto?Con l'uso di una funzione di callback!. Una funzione di callback è una funzione specializzata che viene passata come parametro a un'altra funzione (che invece è generica). Questo permette alla funzione generica di compiere un lavoro specifico attraverso la callback.

Nel nostro caso la funzione generica e' sendRequest(), mentre quella specializzata e' callBack(req).

In questo modo tramite la funzione di callback abbiamo un riferimento all'oggetto req ed ai dati ricevuti tramite la variabile req.responseText, potendo anche specificare di volta in volta una funzione specializzata diversa a seconda della richiesta asincrona che vogliamo compiere.

Supponiamo ad esempio di dover recuperare prima la lista delle citta' e poi la lista dei CAP.Se usassimo direttamente il collegamento req.responseText dentro la funzione generica per smistare i dati nel giusto contenitore dovremmo indicare sia la combobox delle citta' che quella dei CAP introducendo qualche parametro checi permetta di distinguere l'uno o l'altro nei due casi.

Invece se usiamo due funzioni di callback diverse (collegaCity e collegaCap) basta indicare nella chiamata quale callback usare - sendRequest(url, collegaCity) e sendRequest(url, collegaCap).
Dentro la funzione di callback poi collegheremo il testo ricevuto all'elemento (o agli elementi) del DOM opportuni:


supponiamo di avere un combobox vuoto con ID="cap", e di ricevere i seguenti dati come stringa HTML:


<option value ="20100" selected="selected">20100</option>
<option value ="20101">20101</option>
<option value ="20102">20102</option>
<option value ="20103">20103</option>


...e cosi' via. In questo caso collegheremo i dati al combobox in questo modo, sfruttando le funzioni di manipolazione del DOM:


//questa e' la funzione di callback per inserire i dati ricevuti
// in un combobox html <select id="cap"></select>
// Passiamo come parametro l'oggetto XMLHttpRequest inizializzato con la
// variabile req nella chiamata alla funzione
// sendRequest('destinazione.php?city=milano', collegaCap); che abbiamo usato per richiedere la lista dei CAP
// al web server remoto in modo asincrono
function collegaCap(req){

// req.responseText contiene la stringa ritornata dal webserver con la lista dei CAP
var dati=req.responseText;

// ora dobbiamo recuperare un riferimento al combobox usando il metodo document.getElementById()
var combo=document.getElementById('cap');

// inseriamo la stringa html ricevuta dal server come contenuto html interno al tag
// <select></select>
// il browser in questo modo aggiorna la pagina automaticamente
// mostrando il contenuto del nostro combobox con la lista dei CAP
combo.innerHTML=dati;
}

L'ultima domanda che ci poniamo e': come creiamo i dati?

Beh questo e' semplicemente programmazione lato server con un linguaggio dinamico, come facevamo prima.

Cioe' creiamo una pseudo-pagina web che agisca solo come fornitore dati, ovvero che quando viene chiamata restituisca i dati in formato html magari prelevandoli da un database oppure da un webservice.

Ecco fatto!Questo e' Ajax! Semplice, no?
Ma e' tutto qui? Si...in un certo senso.

E' come dire che abbiamo visto come costruire il mattoncino base dei Lego con cui costruire strutture megacomplesse!!!!!

Qual'e' il vantaggio di tutto cio'?

Beh se pensiamo che magari il nostro combobox e' dentro ad una pagina con centinaia di campi diversi che possono essere popolati dinamicamente "al volo" immaginiamo che poter avere a disposizione un meccanismo asincrono di popolamento dinamico "on demand" senza dover ognivolta ricaricare tutto quanto possa essere un modo molto efficiente di gestire la nostra pagina.E soprattutto molto piu' veloce, poiche' dover caricare solo pochi dati per volta piuttosto che centinai di KB per tutta la pagina puo' portare ad un risparmio di tempo considerevole ogni volta.


Questa e' solo una introduzione di base su come iniziare praticamente con Ajax.Resta inteso che occorre approfondire tutto quello che si e' detto qui con il tutorial Ajax vero e proprio, perche' si tratta solo di una introduzione che ha il solo scopo di incuriosirvi e di farvi capire cosa sia Ajax.

2 commenti:

francoventoso ha detto...

Complimenti, ho solo dato uno sguardo superficiale ai tuoi articoli e penso di aver trovato finalmente i chiarimenti e la guida per imparare ad utilizzare Ajax.
Dedicherò il mio tempo libero per studiare con cura i tuoi appunti e ancora grazie per aver condiviso in rete la tua esperienza.
Ciao e a presto
Franco

Massimiliano Modena ha detto...

Ti ringrazio e se avessi qualche suggerimento su come migliorare il tutorial o su punti su cui sia utile scrivere un post mi saresti davvero di aiuto.