Programmare Con AJAX
Programmare Con AJAX
Programmare Con AJAX
LA GUIDA PER PORTARE FACILMENTE LE TUE APPLICAZIONI NEL MONDO DEL WEB 2.0
PROGRAMMARE CON
AJAX
Francesco Smelzo
i libri di
PROGRAMMARE CON
AJAX
Francesco Smelzo
Indice
PROGRAMMARE CON
AJAX
INDICE
Cos Ajax
1.1 flusso tradizionale delle applicazioni Web . . . . . . . . . . . . . . . . . .7 1.2 Perch Ajax una novit? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 1.2.1 Nuove applicazioni di massa sul Web . . . . . . . . . . . . . . .9 1.2.2 pproccio allo sviluppo Web da parte dei programmatori . .10 1.3 Lo scopo del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12 1.4 Conoscenze richieste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
Dom HTML
2.1 ed il Dom? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .18 2.2 Trovare gli elementi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21 2.3 Trovare e impostare gli attributi . . . . . . . . . . . . . . . . . . . . . . . .23 2.4 Creare e rimuovere i nodi . . . . . . . . . . . . . . . . . . . . . . . . . . . .25 2.5 Manipolazione di tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . .30 2.6 Gli eventi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .34 2.7 Associazione di eventi con il DOM . . . . . . . . . . . . . . . . . . . . . .35 2.8 Mettiamo tutto insieme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41
PROGRAMMARE
AJAX
CON
Indice
Lato server
4.1 Il problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .90 4.2 Facciamolo in PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .90 4.3 Facciamolo in classic ASP . . . . . . . . . . . . . . . . . . . . . . . . . . . . .97 4.4 Facciamolo in ASP.NET con C# . . . . . . . . . . . . . . . . . . . . . . . .102 4.5 E se preferiamo VB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .107 4.6 Facciamolo con un Web Service .NET con VB . . . . . . . . . . . . .110
Finalmente AJAX
5.1 Il problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117 5.2 Soluzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117 5.3 Persistenza delle informazioni . . . . . . . . . . . . . . . . . . . . . . . .127 5.4 Noscript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .132 5.5 Altri modi di approccio ad AJAX ASP.NET AJAX . . . . . . . . . .132 5.6 JSON il cugino di AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . .136
PROGRAMMARE CON
Capitolo 1
Cos AJAX
AJAX
COS AJAX
Quando in programmazione si incontrano delle cose semplici e che funzionano, il riflesso incondizionato della comunit degli sviluppatori di renderle complicate e quello delle multinazionali del software di farci anche qualche affare. La prima cosa da sottolineare che AJAX NON un nuovo linguaggio di programmazione, ma piuttosto lutilizzo di un insieme di tecnologie gi esistenti. Da una parte infatti c stata levoluzione dellHTML che, con il supporto dei browser sempre pi moderni, mette a disposizione un Modello ad Oggetti (DOM Document Object Model) che pu essere modificato, anche dopo che una pagina stata caricata nel browser con Javascript, tant che gi dalla versione 4 di internet explorer (1997) si parlava di DHTML ( HTML dinamico). Poi abbiamo avuto il ciclone di XML (1998) che diventato lo strumento per eccellenza per il trasporto dei dati nella programmazione moderna. Per questo insieme di tecnologie (DHTML, Javascript e XML) , nel febbraio 2005, il consulente Jesse James Garrett coni (pare sotto la doccia) il termine di AJAX, che a noi italiani ricorda un detersivo ma in realt un acronimo per Asynchronous JavaScript and XML ovvero XML utilizzato da javascript in modo asincrono (tecnica che, come abbiamo visto, si poteva usare e si usata ben prima del 2005).
Capitolo 1
JSP ecc) non fanno altro che inserire delle istruzioni per lelaborazione delloutput (la pagina Web) tra la Request e la Response. In un meccanismo di questo tipo ogni aggiornamento dei dati presentati richieder una nuova Request con il conseguente ricaricamento (e rielaborazione da parte del server) di tutta la pagina. Pensiamo al semplice caso di una pagina web contenente una maschera per linserimento dei dati. Per verificare la correttezza dei dati inseriti, con la tecnica tradizionale, occorre inviarli al server dove ci sar del codice che ne valider la correttezza e, in caso di problemi, ricostruir lintera pagina indicando allutente dove ha sbagliato. Per mitigare il consumo di risorse in termini di banda e di CPU del server si ricorre di solito a script nella pagina web che controllino i dati in fase di invio. Ma anche questa tecnica ha le sue limitazioni, infatti possibile controllare se un campo vuoto o una data sbagliata, ma non se, ad esempio, un ordine supera la quantit di merce in magazzino o se una password gi esistente nel sistema, semplicemente perch questi dati li ha il server e non il client. Linsieme delle tecnologie AJAX invece consente di risolvere brillantemente il problema. Si carica normalmente la pagina con la maschera in questione dove sar presente uno script che, senza ricaricare la pagina, avvia in background un dialogo con il server per verificare la correttezza dei dati che lutente sta inserendo avvertendolo in tempo reale di un eventuale errore. Tutto questo senza bisogno di ricaricare tutto daccapo.
PROGRAMMARE CON
Capitolo 1
Cos AJAX
AJAX
tanza), diciamo che la novit di AJAX in gran parte dovuta a due fattori: 1. Nuove applicazioni di massa sul web 2. Approccio allo sviluppo Web da parte dei programmatori
PROGRAMMARE
CON
Capitolo 1
Questa ed altre applicazioni del genere, hanno portato una gran massa di sviluppatori in contatto con le possibilit offerte da AJAX ed hanno contribuito in gran parte ad accrescere linteresse verso di esso.
PROGRAMMARE CON
Capitolo 1
Cos AJAX
AJAX
mercato, spostarsi sul web. Anche se il punto darrivo di queste due categorie di sviluppatori lo stesso le condizioni di partenza, e quindi il background culturale, ben diverso. Il programmatore del secondo tipo non avr problemi a gestire connessioni a database, algoritmi sofisticati ecc ma resta praticamente in panne quando ha a che fare con il mondo dei TAG, dei CSS e degli altri standard del mondo Web. Basti pensare quant ancor oggi difficile far digerire ad un programmatore tradizionale la sintassi, tutto sommato banale, di XML. Naturalmente anche il programmatore del primo tipo avr le sue lacune, ma sicuramente con la sintassi HTML e simili si trova molto pi a casa sua. C da dire che delle due tipologie di programmatori Web la seconda in larghissima parte prevalente, e questo lo sta a dimostrare chiaramente levoluzione delle varie tecnologie lato server,ASP.NET, JSP e da ultimo pare anche PHP. Le tecnologie di sviluppo Web di ultima generazione, infatti, tendono a riprodurre il pi fedelmente possibile lambiente di sviluppo tradizionale. Ecco che si parla quindi di Controlli , di Eventi, di Finestre ecc Tutto questo per nascondere il pi possibile allo sviluppatore le particolarit, e le peculiarit dellambiente in cui si trova ad operare per farlo assomigliare sempre di pi ad un normale programma desktop. Ecco quindi che ci si inventa una miriade di POST nascosti (che fanno crescere a dismisura le dimensioni della pagina da scaricare sul browser) per conservare lo stato dei controlli e gestire gli eventi. Il risultato che il programmatore perde progressivamente il contatto con lambiente in cui opera effettivamente e, pur scrivendo per il Web, magari incontra grandissima difficolt ad inserire un semplice script di validazione. Allora chiaro che un programmatore Web di questo tipo che si troI libri di ioPROGRAMMO/Programmare con AJax
11
PROGRAMMARE
CON
Capitolo 1
va davanti una tecnologia tutta incentrata sul Client come AJAX si trova come un pesce fuor dacqua e comincia a cercare spasmodicamente controlli, tools di generazione automatica o di conversione e quantaltro che la fantasia umana pu produrre.
PROGRAMMARE CON
Capitolo 1
Cos AJAX
AJAX
13
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
DOM HTML
HTML sostanzialmente un insieme di regole di formattazione del testo basato su speciali marcatori, o TAG, che definiscono gli elementi da visualizzare. Ad esempio, il codice per visualizzare una tabella con una riga e due celle sar:
<table> <!--inizia tabella --> <tr> <!--inizia riga--> <td> <!--prima Cella--> Nome <!--testo --> </td><!-- fine prima Cella--> <td><!-- seconda Cella --> Cognome <!--testo --> </td><!-- fine seconda Cella--> </tr> <!--fine riga--> </table><!--fine tabella-->
Ogni TAG ha poi una serie di propriet che definiscono lo stile di visualizzazione: la larghezza di un elemento, il colore di sfondo ecc Queste propriet sono scritte come attributi del TAG, per cui volendo dare alla nostra tabella una larghezza di 400 pixel, alla prima cella il colore di sfondo grigio e alla seconda giallo potremo scrivere:
<table width="400"> <tr> <td bgcolor="#EEEEEE"> Nome </td> <td bgcolor="#FFFF66"> Cognome
I libri di ioPROGRAMMO/Programmare con Ajax
15
PROGRAMMARE
CON
Capitolo 2
Lattributo espresso allinterno del marcatore di apertura nella forma nome=valore , il valore espresso tra virgolette semplici o doppie (anche se in HTML, a differenza che in XML, le virgolette non sono obbligatorie per i valori senza spazi). I valori relativi ai colori sono espressi in formato esadecimale, la stringa del colore rappresenta i tre numeri, ognuno da 0 (ovvero 00) a 255 (ovvero FF), che compongono lo spettro RGB (Rosso , Verde, Blu) dei colori primari del monitor, quindi il bianco che 255-255-255 in esadecimale sar #FFFFFF. Ogni TAG, detto anche Elemento, contiene decine di propriet. Lelemento TABLE, ad esempio, contiene (tra le altre) queste propriet:
Attributo
ALIGN BACKGROUND BGCOLOR BORDER BORDERCOLOR CELLPADDING CELLSPACING HEIGHT WIDTH
Descrizione
Allineamento tabella Immagine di sfondo Colore di sfondo Spessore bordo Colore bordo Spazio interno alle celle Spazio tra una cella e laltra Altezza Larghezza
Tutti gli elementi hanno poi degli attributi comuni, tra cui:
Attributo
ID Class Style
Descrizione
Identificativo univoco dellelemento Classe CSS Stile CSS inline
16
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
Laffollamento di attributi necessari per la formattazione di una pagina web ha portato poi alla nascita dei fogli di stile che non sono altro che raccolte di propriet raggruppate per: G Nome del TAG G Nome del Gruppo di regole (Classe) G ID dellelemento Le regole CSS sono espresse nella forma:
nome:valore;nome2:valore2;
E possono essere scritte direttamente nel TAG (modalit inline) usando lattributo style:
<table style=width:400px;background-color:#FFFFFF> oppure inserite nel tag <style> nella pagina stessa: <style> TABLE { width:400px; background-color:#FFFFFF; } .myTable { width:400px; background-color:#FFFFFF; } #table1 { width:400px; background-color:#FFFFFF; } </style>
qui abbiamo inserito le tre modalit di referenziare un gruppo di regole: G la prima si applicher a tutti gli elementi che hanno il nome
I libri di ioPROGRAMMO/Programmare con Ajax
17
PROGRAMMARE
CON
Capitolo 2
TABLE la seconda si applicher a tutti gli elementi che hanno lattributo class uguale al nome assegnato (senza punto) come: <table class=myTable> G la terza si applicher allelemento che ha lid uguale a quello indicato (senza cancelletto) come: <table id=table1>
G
Le regole CSS, oltre allinterno del TAG (nellattributo style) e nel TAG <style>, possono essere definite anche in file separati da quello della pagina Web e poi collegate alla pagina madre con il TAG <link> :
<link type="text/css" rel="stylesheet" href="stili.css">
2.1 ED IL DOM?
Fin qui un breve ripasso (degno di un Bignami) dellHTML, ma il DOM cos? In pratica con altro che la modellizzazione dei vari TAG come gerarchia di oggetti. Come primo oggetto abbiamo Window che rappresenta una finestra del browser (vedi schema in figura 2), ogni Window avr un Document che rappresenta lHTML del documento corrente,
18
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
il quale avr una serie di oggetti Element nidificati fino a rappresentare lalbero HTML. Naturalmente le cose sono un po pi complesse di quanto enunciato, tuttavia ai nostri scopi ci basta sapere che: 1. tutti gli elementi sono visti come oggetti 2. tutti gli attributi degli elementi sono le propriet degli oggetti 3. tutti gli oggetti espongono anche degli eventi Occorre ora soffermarci sul concetto di Nodo. Il nodo rappresenta lentit principale del DOM, relativamente allHTML sono nodi: G il Document stesso G gli Elementi o TAG G gli Attributi degli elementi G il Testo interno agli elementi Un tipo di nodo si riconosce dallaltro per la propriet nodeType che definisce, a seconda del valore, le varie tipologie : 1 = Elemento 2 = Attributo 3 = Testo Il concetto di nodo importante perch attraverso un nodo di possono trovare i nodi figlio e il nodo padre. I nodi che possono contenere sotto-elementi (quindi nodi Elemento o il Document) hanno infatti la propriet childNodes che rappresenta linsieme dei sotto-elementi e del testo e, i nodi elemento, hanno la propriet attributes che rappresenta linsieme degli attributi. Parimenti un nodo ha la propriet parentNode che consente di risalire allelemento padre. Altre propriet comuni a tutti i nodi sono: nodeName ovvero il nome del
I libri di ioPROGRAMMO/Programmare con Ajax
19
PROGRAMMARE
CON
Capitolo 2
Valore
400 #EEEEEE Nome #FFFF66 Cognome
Parent
TABLE TABLE TR TD TD TR TD TD
ChildNodes
TR TD,TD #TEXT #TEXT -
20
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
21
PROGRAMMARE
CON
Capitolo 2
</tr> </table>
A questo punto sar possibile riferirsi, nel codice javascript, a questo elemento come:
var cell1 = document.getElementById("primaCella");
Lid deve naturalmente essere univoco per ogni elemento, nel caso in cui vi siano pi elementi con lo stesso id verrebbe restituito solamente il primo.
getElementsByName getElementsByName anchesso un metodo di document che per consente di recuperare non un singolo elemento ma uninsieme di elementi ( Array) dato il valore del loro attributo name. Dato che lattributo name proprio solo degli elementi di input utente (form, input, select e textarea) la tecnica applicabile solo a questi. Partendo, ad esempio, da questo codice HTML :
<input type="text" name="box"> <input type="text" name="box"> <input type="text" name="box2">
getElementsByTagName getElementsByTagName, al contrario degli altri due metodi che abbiamo visto, non proprio soltanto delloggetto document, ma di tutti gli elementi. Esso consente di recuperare tutti i sottoe22
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
lementi con un TAG di un determinato nome. Sempre rifacendosi allesempio della tabella vista in precedenza, possiamo recuperare il riferimento alla table con getElementById e quindi tutte le celle con getElementsByTagName:
var oTable = document.getElementById("myTable"); var cells = document.getElementsByTagName("TD");
Volendo recuperare da javascript il valore dellattributo title dellelemento DIV sar sufficiente:
var oDiv = document.getElementById("div1"); var attValue = oDiv.getAttribute("title");
23
PROGRAMMARE
CON
Capitolo 2
setAttribute prevede due parametri, il primo il nome dellattributo da impostare o creare ed il secondo il valore che deve assumere.
Le propriet getAttribute e setAttribute sono i metodi standard che offre il DOM per recuperare e impostare gli attributi, con questi metodi possiamo recuperare e impostare attributi anche non presenti nel linguaggio HTML. Se ad esempio avessimo un TAG HTML come:
<div id="div1" categoria="prova">testo</div>
potremmo egualmente recuperare e impostare lattributo categoria con getAttribute e setAttribute anche se esso non previsto dal linguaggio HTML. Questa una risorsa importante per lo sviluppatore perch consente di estendere i TAG secondo le proprie esigenze. Tuttavia quando abbiamo un riferimento ad un elemento HTML attraverso il DOM loggetto risultante espone tutti gli attributi standard del linguaggio anche come propriet. Quindi per recuperare o impostare lattributo title (che un attributo proprio di DIV) avremmo potuto tranquillamente usare:
var oDiv = document.getElementById("div1"); var attValue = oDiv.title; // lettura oDiv.title = "nuovo valore"; // scrittura
Praticamente ogni attributo che pu essere inserito negli elementi HTML si ritrova come propriet negli oggetti referenziati con DOM con lo stesso nome, fa eccezione lattributo class utilizzato per riferirsi a un gruppo di regole CSS, in questo caso infatti la propriet di chiama className. Quindi per impostare la classe CSS di un elemento dinamica24
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
Diverso il discorso per gli stili in linea che, come abbiamo visto, in HTML si esprimono cos:
<div id="div1" style="background:#FFFFFF;textalign:left;color:#0099EE">
nel DOM per ogni elemento esiste la propriet style che per un oggetto a s stante che ha a sua volta una serie di propriet per quante sono le regole definibili con i CSS. Quindi potremo impostare gli stili inline di un elemento con : var oDiv = document.getElementById("div1");
oDiv.style.background = "#FFFFFF"; oDiv.style.textAlign = "left"; oDiv.style.color = "#0099EE";
25
PROGRAMMARE
CON
Capitolo 2
createElement createTextNode c anche createAttribute, ma reso pressoch inutile dal pi comodo setAttribute che abbiamo gi visto. Con createElement possibile creare da zero un nuovo oggetto e impostarne che propriet. Ecco come creare un nuovo elemento DIV utilizzando il DOM con javascript:
G G
var oDiv = document.createElement("div"); oDiv.id = "div1"; oDiv.style.background = "#FFFFFF"; oDiv.style.textAlign = "left"; oDiv.style.color = "#0099EE";
Il parametro del metodo createElement appunto il nome dellelemento. Nello stesso modo possibile anche creare dinamicamente un nodo di testo (cio del testo) :
var txt = document.createTextNode("Questo un testo");
dove il parametro sar invece la stringa valore del nodo. Possiamo cos creare nuovi elementi o nodi di testo ma se ci fermassimo qui questi oggetti verrebbero solamente instanziati in memoria e non visualizzati nel browser. Per far s che questo succeda occorre collocarli, cio inserirli allinterno di un elemento. Per questa operazione tutti gli elementi dispongono di alcuni metodi: G appendChild che consente di inserire un nodo sotto ad un altro G insertBefore che consente di inserire un nodo prima di un altro
26
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
replaceNode che consente di sostituire un nodo con un altro Qui ci limiteremo a prendere in considerazione il primo metodo e vediamo come aggiungere un nuovo elemento a questo DIV esistente :
<div id="container"></div>
In javascript creeremo quindi un nuovo DIV e lo inseriremo sotto quello esistente con :
var oDiv = document.createElement("div"); oDiv.id = "div1"; oDiv.style.background = "#FFFFFF"; oDiv.style.textAlign = "left"; oDiv.style.color = "#0099EE"; var divEsistente = document.getElementById("container"); divEsistente.appendChild(oDiv); //appende l'elemento oDiv
Il risultato visibile nel browser sar quindi come se noi avessimo scritto in HTML :
<div id="container"> <div id="div1" style="background:#FFFFFF;textalign:left;color:#0099EE"></div> </div>
innerHTML La creazione di nodi con createElement non per lunico modo di aggiungere dinamicamente dei contenuti agli elementi gi esistenti, il DOM dota infatti gli elementi della propriet innerHTML che pu essere usata per aggiungere frammenti di coI libri di ioPROGRAMMO/Programmare con Ajax
27
PROGRAMMARE
CON
Capitolo 2
dice HTML in forma di stringa a elementi gi esistenti. Il codice che abbiamo visto in precedenza, per aggiungere un DIV sotto ad un altro, con innerHTML, si sarebbe potuto esprimere con:
var divEsistente = document.getElementById("container"); divEsistente.innerHTML='<div id="div1" style="background:#FFFFFF;' + 'text-align:left;color:#0099EE"></div>';
si risparmiano un bel po di righe di codice ma si rende lo script meno lineare. innerHTML ha per anche altri risvolti pratici, ad esempio pu essere usato per inserire del testo direttamente senza usare il metodo document.createTextNode() . Con createTextNode per aggiungere del testo avremmo dovuto scrivere:
var oDiv = document.createElement("div"); oDiv.id = "div1"; oDiv.style.background = "#FFFFFF"; oDiv.style.textAlign = "left"; oDiv.style.color = "#0099EE"; var txt = document.createTextNode("Questo un testo"); oDiv.appendChild(txt); var divEsistente = document.getElementById("container"); divEsistente.appendChild(oDiv); //appende l'elemento oDiv
28
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
oDiv.style.textAlign = "left"; oDiv.style.color = "#0099EE"; oDiv.innerHTML="Questo un testo"; var divEsistente = document.getElementById("container"); divEsistente.appendChild(oDiv); //appende l'elemento oDiv
La propriet innerHTML viene poi utilizzata anche per rimuovere tutto il contenuto di un elemento (sotto-elementi e nodi di testo) in modo rapido. Se abbiamo del codice HTML come questo :
<div id="container"> <div id="div1" style="background:#FFFFFF;textalign:left;color:#0099EE">testo</div> <div id="div2" style="background:#FFFFFF;textalign:left;color:#0099EE">Altro testo</div> </div>
ed il risultato sar :
<div id="container"></div>
Rimuovere i nodi Al di l del piccolo trucco che abbiamo visto per la rimozione del contenuto di un elemento utilizzando la propriet innerHTML il DOM offre anche un modo pi orientato agli oggetti. Ogni elemento dispone infatti dei metodi:
I libri di ioPROGRAMMO/Programmare con Ajax
29
PROGRAMMARE
CON
Capitolo 2
G G
removeChild per rimuovere un nodo figlio removeNode per rimuovere il nodo stesso Quindi per rimuovere il DIV con id div2 da:
<div id="container"> <div id="div1" style="background:#FFFFFF;textalign:left;color:#0099EE">testo</div> <div id="div2" style="background:#FFFFFF;textalign:left;color:#0099EE">Altro testo</div> </div>
potremo usare :
var divContainer = document.getElementById("container"); var div2 = document.getElementById("div2"); divContainer.removeChild(div2);
oppure:
var div2 = document.getElementById("div2"); div2.removeNode();
30
COMPRENDERE
Capitolo 2
DOM HTML
XML
<td> <!--prima Cella--> Nome <!--testo --> </td><!-- fine prima Cella--> <td><!-- seconda Cella --> Cognome <!--testo --> </td><!-- fine seconda Cella--> </tr> <!--fine riga--> </table><!--fine tabella-->
Una cosa che gli sviluppatori a volte ignorano o dimenticano poi che esistono degli elementi che raggruppano le righe in base alla loro collocazione: THead Testa della tabella TBody Corpo della tabella G TFoot Piede della tabella
G G
Praticamente un po come avviene in Word con lintestazione e il pi di pagina. Se questi elementi non vengono definiti in HTML il parser del Browser li aggiunge automaticamente, inserendo tutte le righe nel TBody. Lesempio precedente sarebbe letto dal browser come :
<table> <!--inizia tabella --> <thead></thead> <tbody> <tr> <!--inizia riga--> <td> <!--prima Cella--> Nome <!--testo --> </td><!-- fine prima Cella--> <td><!-- seconda Cella --> Cognome <!--testo -->
I libri di ioPROGRAMMO/Programmare con Ajax
31
PROGRAMMARE
CON
Capitolo 2
</td><!-- fine seconda Cella--> </tr> <!--fine riga--> </tbody> <tfoot></tfoot> </table><!--fine tabella-->
Questo dettaglio importante perch quando, attraverso il DOM, abbiamo il riferimento a una riga (elemento tr):
var oTr = document.getElementById("riga1");
e invece no, perch nel DOM il parent di tr sempre tbody, quindi per trovare il riferimento alla tabella avremmo dovuto scrivere:
var oTr = oTr.parentNode.parentNode;
Essendo composta di elementi come gli altri, una tabella pu essere gestita con il DOM. Ad esempio possiamo crearla con:
var oTable = document.createElement("table"); var oTr = document.createElement("tr"); var oTd = document.createElement("td"); oTd.innerHTML= "Nome"; oTr.appendChild (oTd); var oTd = document.createElement("td"); oTd.innerHTML= "Cognome"; oTr.appendChild (oTd);
32
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
oTable.appendChild (oTr);
Table Object Model Gli elementi che compongono la tabella, probabilmente per il largo uso che se ne fa, oltre al DOM (che resta sempre utilizzabile) dispongono anche di un altro modello ad oggetti: il Table Object Model. Cio, in pratica, hanno dei metodi e propriet proprie. Vediamo quali.
Metodi per la manipolazione di tabelle
createTHead deleteTHead createTFoot deleteTFoot insertRow deleteRow insertCell deleteCell Crea un elemento tHead nella tabella. Cancella un elemento tHead dalla tabella. Crea un elemento tFoot element nella tabella. Cancella un elemento tFoot dalla tabella. Crea una nuova riga nella tabella e la aggiunge allinsieme rows. Cancella la riga dalla tabella e la rimuove dallinsieme rows. Crea una nuova cella in una riga e la aggiunge allinsieme cells. Cancella la cella da una riga e la rimuove dallinsieme cells.
insertRow e insertCell, hanno anche un parametro che definisce la posizione in cui inserire loggetto, il valore -1 indica che deve essere inserito in coda. Per cui, ad esempio, il codice:
var oTr = oTable.insertRow(-1);
33
PROGRAMMARE
CON
Capitolo 2
Righe e celle hanno poi propriet di indice che indicano la loro posizione.
Indici
Posizione dellelemento nella gerarchia di una tabella. Indice della riga nel contesto della tabella a cui appartiene. sectionRowIndex Indice della riga nel contesto allinterno della sezione THead,TBody o TFoot. Indice della cella nel contesto della riga a cui appartiene. cellIndex sourceIndex rowIndex
Con questi strumenti lavorare sulle tabelle diventa pi semplice. Ecco come si presenterebbe il nostro codice usando il Table Object Model:
var oTable = document.createElement("table"); var oTr = oTable.insertRow(-1); var oTd = oTr.insertCell(-1); oTd.innerHTML= "Nome"; var oTd = oTr.insertCell(-1); oTd.innerHTML= "Cognome";
Notate come i metodi insert di righe e celle provvedano direttamente allappend delloggetto allinsieme relativo.
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
segnali sono associate delle funzioni script queste saranno eseguite. In HTML gli eventi sono espressi come normali attributi dei TAG. Un bottone, ad esempio, pu dichiarare una funzione associata allevento onclick semplicemente con:
<button id="btn1" onclick="cliccami()">Clicca qui!</button>
nella pagina ci sar quindi uno script allinterno del quale verr definita la funzione cliccami():
<script language="javascript" type="text/javascript"> function cliccami(){ alert("Mi hai cliccato!"); } </script>
35
PROGRAMMARE
CON
Capitolo 2
tutti tre i modi assegnano una funzione allevento tuttavia: G il primo modo assegna un riferimento ad una funzione definita da qualche altra parte (si riconosce che un riferimento perch sono assenti le parentesi) G il secondo modo assegna direttamente una funzione costruita sul posto G il terzo modo utilizza il costruttore new per un nuovo oggetto Function che ha il corpo passato come parametro Il secondo ed il terzo modo sono utili in particolare con il DOM. Il secondo decisamente il pi usato tuttavia presenta un inconveniente particolare : allinterno della funzione costruita le variabili esterne non contano pi. Cercheremo di spiegarci meglio con un esempio. Abbiamo un ciclo con cui si crea una serie di bottoni, cliccando su ogni bottone deve apparire un messaggio con un numero progressivo diverso, quindi implementiamo il seguente codice:
for(var i=0;i<4;i++){ var oButton = document.createElement("button"); oButton.value= "bottone " + i; oButton.onclick = function (){ alert("Hai premuto il bottone " + i); } }
bene, questo codice non funzioner come desiderato perch allinterno della funzione la variabile i non ha alcun senso poich la funzione verr eseguita nel contesto dellelemento non in quello globale. In questi casi si deve quindi ricorrere al costruttore di funzione che permettere di comporre il corpo della stessa come stringa:
for(var i=0;i<4;i++){
36
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
var oButton = document.createElement("button"); oButton.value= "bottone " + i; oButton.onclick = new Function ( 'alert("Hai premuto il bottone "' + i + ')' ); }
Associazione multipla di eventi Quella che abbiamo visto prima la modalit di associazione singola, evento/funzione. I browser per supportano anche lassociazione multipla, cio pi funzioni associate allo stesso evento. Purtroppo per lo fanno in maniera radicalmente differente. Internet Explorer Internet Explorer segue una strada proprietaria attraverso il metodo attachEvent disponibile per tutti gli oggetti. Vediamo come assegnare pi eventi in Internet Explorer:
var oButton = document.createElement("button"); oButton.attachEvent("onclick",cliccami); oButton.attachEvent("onclick",function (){ alert("secondo messaggio") });
come possiamo vedere il primo argomento del metodo il nome letterale dellevento, mentre il secondo una funzione indicata come riferimento o scritta direttamente.
Firefox e gli altri
I browser pi ligi agli standard realizzano invece lassociazione multipla ad un evento con il metodo addEventListener, anchesso proprio di tutti gli oggetti DOM. Luso simile a quello di attachEvent :
var oButton = document.createElement("button");
I libri di ioPROGRAMMO/Programmare con Ajax
37
PROGRAMMARE
CON
Capitolo 2
Come possiamo notare il nome dellevento espresso senza il prefisso on e c un terzo parametro booleano; il terzo parametro se impostato a true indica che levento viene gestito direttamente dalloggetto e non viene notificato ai livelli superiori. Se, ad esempio, abbiamo un DIV nidificato in un altro e tutti e due avessero associata una funzione allevento onclick lassociazione con addEventListener effettuata sullelemento pi interno, se dichiarata con il terzo parametro a true, significherebbe che il click sullelemento non darebbe luogo allazione prevista dallevento assegnato allelemento superiore.
Mettere daccordo tutti
In questo come in altri casi dello scripting Client-side le differenze tra i vari browser trasformano la vita del programmatore in un inferno, per fortuna c una libreria di funzioni chiamata IEEmu (http://webfx.eae.net/dhtml/ieemu) che permette di utilizzare la sintassi di Internet Explorer anche con gli altri browser.
onload Tutti gli oggetti del DOM hanno associati una serie di eventi, quello che per per noi ha unimportanza cruciale levento onload associato alloggetto window. Per capirne meglio limportanza analizziamo la seguente pagina HTML :
<html> <head> <title>Esempio</title>
38
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
<script language="javascript" type="text/javascript"> var div1 = document.getElementById("div1"); alert(div1.innerHTML); </script> </head> <body> <div id="div1">Testo</div> </body> </html>
come ormai avrete capito lo script dovrebbe trovare lelemento con id div1 e mostrarne il contenuto in un messaggio. Questo script invece non funzioner. Non funzioner perch, trovandosi a livello globale, verr eseguito prima ancora che il documento HTML sia stato completamente caricato ed analizzato dal browser, in questa fase il DOM non disponibile (proprio perch il documento non stato ancora analizzato). Ed qui che entra in gioco levento onload; onload infatti un evento che si verifica quando il documento completamente caricato e analizzato e quindi DOM disponibile. Il nostro codice quindi, per funzionare, avrebbe dovuto essere scritto cos:
<html> <head> <title>Esempio</title> <script language="javascript" type="text/javascript"> window.onload = function () { var div1 = document.getElementById("div1"); alert(div1.innerHTML); } </script> </head>
I libri di ioPROGRAMMO/Programmare con Ajax
39
PROGRAMMARE
CON
Capitolo 2
Quindi : quando le azioni che coinvolgono il DOM non sono collegate ad altro evento (come il click su un bottone ecc) devono essere eseguite nel contesto dellevento onload di window.
Altri eventi Ogni oggetto DOM ha associati una serie di eventi; qualcuno specifico per la funzione che deve svolgere quel determinato elemento, altri comuni un po a tutti gli elementi. Tra gli eventi pi comuni troviamo: G onkeydown quando lutente preme un tasto G onkeypress quando lutente preme un tasto alfanumerico G onkeyup quando lutente rilascia un tasto G oncontextmenu quando lutente fa click con il tasto destro G onclick quando lutente fa click sulloggetto con il tasto sinistro del mouse G ondblclick quando lutente fa doppio click sulloggetto con il tasto sinistro del mouse G onmousedown quando lutente fa click sulloggetto con qualsiasi tasto del mouse G onmouseenter quando lutente si sposta con il mouse nellarea delloggetto G onmouseleave quando lutente lascia con il mouse nellarea delloggetto G onmousemove quando lutente si muove con il mouse nellarea delloggetto G onmouseout quando lutente lascia con il mouse i limiti dellarea delloggetto G onmouseover quando lutente entra con il mouse nei limi40
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
ti dellarea delloggetto onmouseup quando lutente rilascia qualsiasi tasto del mouse, in precedenza premuto, nellarea delloggetto
41
PROGRAMMARE
CON
Capitolo 2
</html>
Notare come abbiamo definito degli id per ogni elemento, lo stile CSS inline con la propriet display su none (nascosto) per il DIV e lassociazione allevento onclick per BUTTON.
Passo 2 codice DOM per la creazione della tabella Allinterno dellelemento <script> dichiariamo un oggetto globale currentTable, inizialmente a null, e scriviamo una funzione createTable che crea dinamicamente la tabella , con dati presi da un array, lappende al DIV e lassegna alla variabile globale currentTable:
var currentTable = null; function createTable(){ //definizione dati tabella //prima riga var headers = ["nome","cognome","indirizzo"]; //dati strutturati come Array di Array var data = [
42
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
["Mario", "Rossi" , "via Dante 2 Milano"], ["Alfredo", "Bianchi" , "via Verga 5 Firenze"], ["Giovanni", "Verdi" , "via Foscolo 10 Roma"], ["Francesco", "Gialli" , "via Pascoli 7 Napoli"] ]; var oTable = document.createElement("TABLE"); //propriet tabella oTable.width = "400"; oTable.cellSpacing = "0"; oTable.cellPadding = "2"; //prima riga var oTr = oTable.insertRow(-1); for(var i=0;i<headers.length;i++){ var oTd = oTr.insertCell(-1); oTd.align="center"; oTd.background = "#EEEEEE"; oTd.style.font = "bold 12px arial"; oTd.style.borderBottom = "1px solid #666666"; oTd.innerHTML= headers[i]; } //righe dati for(var i=0;i<data.length;i++){ var rowData = data [i]; //array riga dati var oTr = oTable.insertRow(-1); for(var n=0;n<rowData.length;n++){ var oTd = oTr.insertCell(-1); oTd.style.font = "12px verdana"; oTd.style.borderBottom = "1px solid #999999"; oTd.innerHTML= rowData[n]; } } currentTable = oTable; var container = document.getElementById("container");
I libri di ioPROGRAMMO/Programmare con Ajax
43
PROGRAMMARE
CON
Capitolo 2
container.appendChild(currentTable); }
Scriviamo poi la funzione cliccami, gi associata allevento onclick di BUTTON, che si occupa di richiamare createTable se currentTable null e di gestire la visibilit del DIV:
function cliccami(){ //instanzia i riferimenti var container = document.getElementById("container"); var btn1 = document.getElementById("btn1"); //controlla currentTable ed eventualmente la crea if(currentTable==null) createTable(); //imposta visibilit del DIV if (container.style.display=="none") { container.style.display=""; btn1.value = "Clicca qui per nascondere la tabella!" } else { container.style.display="none"; btn1.value = "Clicca qui per vedere la tabella!" } } Passo 3 controllare il risultato Alla fine il codice completo della nostra pagina sar : <html> <head> <title>Esempio</title> <script language="javascript" type="text/javascript"> var currentTable = null; function createTable(){ //definizione dati tabella //prima riga
44
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
var headers = ["nome","cognome","indirizzo"]; //dati strutturati come Array di Array var data = [ ["Mario", "Rossi" , "via Dante 2 Milano"], ["Alfredo", "Bianchi" , "via Verga 5 Firenze"], ["Giovanni", "Verdi" , "via Foscolo 10 Roma"], ["Francesco", "Gialli" , "via Pascoli 7 Napoli"] ]; var oTable = document.createElement("TABLE"); //propriet tabella oTable.width = "400"; oTable.cellSpacing = "0"; oTable.cellPadding = "2"; //prima riga var oTr = oTable.insertRow(-1); for(var i=0;i<headers.length;i++){ var oTd = oTr.insertCell(-1); oTd.align="center"; oTd.background = "#EEEEEE"; oTd.style.font = "bold 12px arial"; oTd.style.borderBottom = "1px solid #666666"; oTd.innerHTML= headers[i]; } //righe dati for(var i=0;i<data.length;i++){ var rowData = data [i]; //array riga dati var oTr = oTable.insertRow(-1); for(var n=0;n<rowData.length;n++){ var oTd = oTr.insertCell(-1); oTd.style.font = "12px verdana"; oTd.style.borderBottom = "1px solid #999999"; oTd.innerHTML= rowData[n]; }
I libri di ioPROGRAMMO/Programmare con Ajax
45
PROGRAMMARE
CON
Capitolo 2
} currentTable = oTable; var container = document.getElementById("container"); container.appendChild(currentTable); } function cliccami(){ //instanzia i riferimenti var container = document.getElementById("container"); var btn1 = document.getElementById("btn1"); //controlla currentTable ed eventualmente la crea if(currentTable==null) createTable(); //imposta visibilit del DIV if (container.style.display=="none") { container.style.display=""; btn1.value = "Clicca qui per nascondere la tabella!" } else { container.style.display="none"; btn1.value = "Clicca qui per vedere la tabella!" } } </script> </head> <body> <button id="btn1" onclick="cliccami()">Clicca qui per vedere la tabella!</button> <div id="container" style="display:none"></div> </body> </html>
Se eseguiamo la pagina nel browser, cliccando sul bottone potremo vedere la tabella che appare (o, se visibile, scompare) co46
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 2
DOM HTML
AJAX
me in figura B
47
PROGRAMMARE CON
Capitolo 3
AJAX
Per la scrittura di un documento Well Formed dobbiamo seguire poche ma importanti regole sintattiche, un documento non Well Formed diventa infatti inutilizzabile dai programmi che dovranno utilizzarlo:
Regola numero 1 elemento radice Il documento deve avere uno ed un solo elemento radice (root element) ad esempio:
<biblioteca> <libro>Guerra e pace</libro> <libro>Odissea</libro> </biblioteca>
Abbiamo uno e un solo elemento (biblioteca) come radice. Un documento invece del tipo:
I libri di ioPROGRAMMO/Programmare con Ajax
49
PROGRAMMARE
CON
Capitolo 3
Dove lelemento <libro> deve trovare corrispondenza nel tag di chiusura </libro> allo stesso livello. Senza il tag di chiusura verrebbe generato un errore dai programmi che tentino di leggerlo.
Regola numero 3 elementi vuoti Se un elemento vuoto, nel senso che non contiene testo o altri elementi, la chiusura pu avvenire anche con la sintassi abbreviata: <libro/>. Che sarebbe equivalente a <libro></libro>. Regola numero 4 attributi tra virgolette Gli attributi devono essere racchiusi tra virgolette doppie (") o semplici ('). Entrambe le virgolette utilizzate devono essere dello stesso tipo ( doppie o semplici). Ad esempio, questo metodo per definire gli attributi corretto:
<libro autore="Verga">I Malavoglia</libro>
PROGRAMMARE CON
Capitolo 3
AJAX
Gli elementi non possono essere chiusi prima di chiudere i sotto-elementi in essi contenuti. Questo un esempio di sintassi non valida:
<biblioteca> <narrativa> <libro autore="Verga"> I Malavoglia </narrativa> </libro> </biblioteca>
Perch lelemento narrativa viene chiuso prima dellelemento libro che inizia al suo interno. La sintassi corretta sarebbe stata invece:
<biblioteca> <narrativa> <libro autore="Verga"> I Malavoglia </libro> </narrativa> </biblioteca>
Regola numero 6 gli elementi sono case-sensitive I TAG di apertura e di chiusura devono avere il nome scritto con lettere dello stesso tipo (maiuscole/minuscole), ad esempio lelemento: <libro></libro> corretto, mentre non lo :<LIBRO></libro>.
51
PROGRAMMARE
CON
Capitolo 3
tato dai programmi che analizzano lXML (parser). I nodi Anche qui ritroviamo il concetto di Nodo che ha tre propriet fondamentali: G nodeType il tipo di nodo G nodeValue il valore del nodo G nodeName il nome del nodo Loggetto Node la base per tutti gli altri oggetti che ne derivano per estensione (attributi, elementi ecc). Esso dispone quindi di propriet e di metodi comuni anche agli oggetti derivati:
Propriet
attributes childNodes firstChild lastChild namespaceURI nextSibling nodeName nodeType nodeValue ownerDocument parentNode prefix previousSibling Un oggetto NamedNodeMap contenente tutti gli attributi di un nodo Un oggetto NodeList contenente tutti i nodi figlio Il primo nodo figlio Lultimo nodo figlio L URI del namespace di un nodo Il nodo immediatamente seguente a un nodo allo stesso livello Il nome di un nodo Il tipo (costante numerica) di un nodo Il valore del nodo Loggetto Document di un nodo Il nodo che contiene il nodo Il prefisso del namespace di un nodo Il nodo immediatamente precedente a un nodo allo stesso livello
Metodi
appendChild(newnode) cloneNode(boolean) hasChildNodes() insertBefore(newnode,re fnode) Aggiunge un nuovo nodo figlio a un nodo e lo restituisce Crea una copia esatta di un nodo. Se il parametro true riproduce anche i nodi figli Restituisce true o false a seconda del il nodo ha nodi figli
52
PROGRAMMARE CON
Capitolo 3
AJAX
removeChild(nodename) Rimuove il nodo figlio specificato e lo restituisce replaceChild(newnode,ol Sostituisce il vecchio nodo (oldnode) con il nuovo dnode) (newnode) e restituisce il vecchio nodo
Ovviamente tali propriet e metodi avranno un senso diverso a seconda del tipo di nodo a cui si applicano.Ad esempio la propriet attributes di un nodo di tipo Attr (attributo) restituir null ecc Dalloggetto Node derivano, come abbiamo detto, gli altri oggetti che rappresentano particolari tipi di nodi, tra cui document, element e attribute.
Oggetto Document (derivato da Node) Document rappresenta il documento XML stesso.
Propriet
doctype documentElement Il DTD o lo Schema del documento Loggetto Element che rappresenta lelemento radice del documento
Metodi
createAttribute("name") createCDATASection("text") createComment("text") createDocumentFragment() createElement("name") createEntityReference("name") createProcessingInstruction (target,text) createTextNode("text") getElementById("id") getElementsByTagName ("name") Crea un nuovo nodo attributo Crea un nuovo nodo CDATA Crea un nuovo nodo commento Crea un oggetto vuoto documentFragment Crea un nuovo nodo elemento Crea un nuovo nodo entityReference Crea un nuovo nodo processingInstruction Crea un nuovo nodo text Restituisce il nodo corrispondente a un id Restituisce un oggetto NodeList di tutti gli oggetti Nodo che hanno il nome uguale a quello specificato.
53
PROGRAMMARE
CON
Capitolo 3
Se un elemento ha del testo contenuto questo viene visto come nodo figlio di tipo testo (non come nodeValue).
Propriet
tagName Nome dellelemento (lo stesso valore di nodeName)
Metodi
getAttribute(name) getAttributeNode(name) Restituisce il valore dellattributo specificato Restituisce lattributo specificato come oggetto Attribute getElementsByTagName(name) Restituisce un oggetto NodeList di tutti gli oggetti Nodo che hanno il nome uguale a quello specificato. hasAttribute() Restituisce true o false a seconda se lelemento ha attributi normalize() Unisce tutti i nodi di Testo sottostanti in un unico nodo di Testo removeAttribute(name) Rimuove il valore dellattributo specificato removeAttributeNode(name) Rimuove lattributo specificato setAttribute(name,value) Imposta il valore dellattributo specificato setAttributeNode(name) Inserisce un nuovo attributo
PROGRAMMARE CON
Capitolo 3
AJAX
Propriet
name value nome dellattributo (lo stesso valore di nodeName) Restituisce o imposta il valore dellattributo
55
PROGRAMMARE
CON
Capitolo 3
Milano"/> <persona nome="Alfredo" cognome="Bianchi" indirizzo="via Verga 5 Firenze"/> <persona nome="Giovanni" cognome="Verdi" indirizzo="via Foscolo 10 Roma"/> <persona nome="Francesco" cognome="Gialli" indirizzo="via Pascoli 7 Napoli"/> </root>
Internet Explorer
Vediamo come Internet Explorer consente di accedere ai dati come oggetto DOM:
function apriDomIE(docPath){ var objDOM = new ActiveXObject("Msxml2.DOMDocument"); objDOM.async=false objDOM.load(docPath); return objDOM; }
In pratica si instanzia lengine nativo MSXML, installato nei sistemi Windows, e si utilizza il metodo load per caricare il documento. I dati sono stati caricati in modo sincrono, il flusso cio aspetta fino a che il caricamento non viene completato, ma IE offre anche la possibilit di caricamenti asincroni:
var xmlDoc=null; function apriDomIEAsync(docPath){ xmlDoc = new ActiveXObject("Msxml2.DOMDocument"); xmlDoc.onreadystatechange = readystatechange; xmlDoc.load(docPath); } function readystatechange(){
56
PROGRAMMARE CON
Capitolo 3
AJAX
In pratica loggetto viene dichiarato a livello globale (variabile xmlDoc) e nella funzione che lo istanzia associata una funzione di callback allevento onload quindi viene fatto partire il caricamento, quando esso termina viene eseguita la funzione di callback con sulloggetto che nel frattempo sar stato valorizzato.
Firefox
In Firefox invece il caricamento del file XML avviene in sempre modo asincrono, quindi avremo:
var xmlDoc=null; function apriDomFF(docPath){ var state = 0; xmlDoc = document.implementation.createDocument("", "", null) xmlDoc.onload = _loaded; xmlDoc.load(docPath); } function _loaded(){ var nome = xmlDoc.documentElement.firstChild.getAttribute("nome"); alert(nome); } apriDomFF("data.xml");
Asincrono o sincrono?
I libri di ioPROGRAMMO/Programmare con Ajax
57
PROGRAMMARE
CON
Capitolo 3
I puristi affermano che limplementazione di Firefox la migliore il caricamento asincrono consente di non bloccare linterfaccia utente mentre si stanno caricando i dati da un file remoto. In parte ci vero, ma a volte nella vita reale le cose sono un po diverse. Pensate ad un caso niente affatto ipotetico: una funzione deve caricare un piccolo file XML contenente le preferenze dellutente, leggerle e individuare la path di un file di dati pi grande e caricarlo. Con solo il metodo asincrono avremmo due caricamenti in successione, con due callback nidificati e con un notevole aumento della complessit del codice (per niente giustificato, visto che il primo file era solo di pochi Kb). Quindi, siamo daccordo che il metodo asincrono generalmente il migliore, ma perch non lasciarci la possibilit di scegliere? A volte linterfaccia utente deve rimanere bloccata fino a che non abbiamo acquisito i dati!
Caricamento di XML da una stringa Abbiamo visto come caricare un file XML, ma a volte potrebbe essere necessario creare un oggetto DOM da una stringa XML in memoria. Anche qui (verrebbe da dire : naturalmente) i metodi adottati dai vari browser sono diversi.
Internet Explorer
In Internet Explorer il caricamento da stringa si fa con il metodo loadXml delloggetto DOMDocument, ovvero:
function loadXmlIE(){ var xmlDoc = new ActiveXObject("Msxml2.DOMDocument"); xmlDoc.async = false; s ='<root><persona nome="Mario" cognome="Rossi" indirizzo="via Dante 2 Milano"/>';
58
PROGRAMMARE CON
Capitolo 3
AJAX
<persona nome="Alfredo" cognome="Bianchi" <persona nome="Giovanni" cognome="Verdi" indirizzo="via Foscolo 10 Roma"/>'; <persona nome="Francesco" cognome="Gialli" indirizzo="via Pascoli ..."/>';
indirizzo="..."/>';
Firefox
59
PROGRAMMARE
CON
Capitolo 3
Manipolazione dei dati In qualsiasi modo abbiate ottenuto il vostro oggetto DOM potete finalmente star sicuri che propriet e metodi standard saranno disponibili: infatti malgrado le differenze nel caricamento dei dati loggetto che ne risulter avr certamente quelle caratteristiche di cui abbiamo parlato prima. Ci saranno quindi i metodi per leggere e impostare gli attributi (getAttribute e setAttribute), gli insiemi di elementi e nodi di testo childNodes, i metodi per creare nuovi elementi (createElement) e aggiungerli a nodi esistenti (appendChild) e cos via Tuttavia, con il solo DOM XML, una cosa risulta particolarmente scomoda: trovare il riferimento ad un nodo o ad un insieme di nodi. Pensate dover trovare tutti gli elementi <libro> appartenti al reparto Classici in un file come questo:
<?xml version="1.0"?> <biblioteca> <reparto nome="Classici"> <libro> <autore>Omero</autore> <titolo>Odissea</titolo> </libro> <libro> <autore>Omero</autore> <titolo>Iliade</titolo> </libro> </reparto> <reparto nome="Fantasy"> <libro> <autore>J.R.R. Tolkien</autore> <titolo>Il signore degli Anelli</titolo> <editore>Mondadori</editore> </libro>
60
PROGRAMMARE CON
Capitolo 3
AJAX
</reparto> </biblioteca> in javascript dovreste scrivere qualcosa tipo : var doc = loadXml ("biblioteca.xml"); var reparti = doc.getElementsByTagName("reparto") for(i=0;i<reparti.length;i++){ var reparto = reparti[i]; //nodo <reparto> if( reparto.getAttribute("nome")=="Classici") { var libri = reparto.getElementsByTagName("libro") for(j=0;j<libri.length;j++){ var libro = libri[j]; //nodo <libro> //... } } }
cio implementare una serie di cicli nidificati con regole interne di confronto per selezionare le informazioni. No. Decisamente serve qualcosa di pi pratico Per vostra (e nostra) fortuna esiste XPath che un linguaggio simile a quello che per SQL sono le query di selezione.
3.3 XPATH
XPath dotato di una sintassi basata sui percorsi (Path appunto) che consente di selezionare e filtrare i nodi in base alla loro posizione, ai loro attributi ecc La sua filosofia sta nel tradurre tutto in path, nel file che abbiamo visto in precedenza, ad esempio, i nodi <libro> hanno tutti la path: biblioteca/reparto/libro e attraverso questa path sar possibile ottenere un insieme di nodi (nodeList). relazioni tra i nodi XPath prende in considerazione innanzitutto le relazioni tra i nodi
I libri di ioPROGRAMMO/Programmare con Ajax
61
PROGRAMMARE
CON
Capitolo 3
che possono essere : G Parent - ogni Element (tranne quello radice) , Attribute o TextNode ha un genitore G Children - i nodi di tipo Element possono avere uno o pi nodi figli G Siblings - i nodi di tipo Element hanno una relazione Sibling con gli elementi di pari livello. G Ancestor - sono Ancestors (ascendenti) di un elemento tutti gli elementi che lo precedono, fino alla radice (che il primo elemento) G Descendants - sono Descendants (discendenti) di un elemento tutti gli elementi che racchiude
Selezione dei nodi XPath espressione di un percorso (come le path del sistema operativo) in un documento XML. Il nodo selezionato seguendo il suo percorso. Ecco alcune delle path expressions pi utilizzate:
Espressione
nome / //
Descrizione
Seleziona tutti gli elementi figli del nodo con il nome corrispondente a quello indicato Seleziona dal nodo radice Seleziona i nodi nel documento dal nodo corrente che corrisponde alla selezione indipendente dalla posizione in cui siamo Seleziona il nodo corrente Seleziona nodo Parent del nodo corrente Rappresenta un attributo con il nome corrispondente a quello indicato
. .. @nome
Esempi :
62
PROGRAMMARE CON
Capitolo 3
AJAX
path expression
/biblioteca /biblioteca/reparto //autore //titolo/@lingua
Risultato
Tutti i nodi <biblioteca> a partire dalla radice Seleziona tutti i nodi < reparto> sotto a <biblioteca> a partire dalla radice Seleziona tutti i nodi <autore> indipendentemente da dove si trovino Seleziona lattributo lingua di tutti i nodi <titolo>
Predicati I predicati sono usati per trovare un nodo specifico o un nodo che contiene (o ha un attributo che contiene) un dato valore. I predicati devono essere contenuti in parentesi quadre [].
Esempi:
path expression
/biblioteca/reparto [1]
Risultato
Seleziona il primo nodo <reparto> sotto a <biblioteca> /biblioteca/reparto [last()] Seleziona lultimo nodo <reparto> sotto a <biblioteca> Seleziona i nodi <titolo> che hanno lattributo //titolo[@lingua='en'] lingua con valore en //libro[titolo/@lingua='en'] Seleziona i nodi <autore> sotto <libro> che ha lattributo lingua con valore en /autore nellelemento <titolo>
Selezionare nodi non conosciuti Dei metacaratteri (wildcards) possono essere usati al posto del nome del nodo
Espressione
* @* node()
Descrizione
Qualsiasi elemento Qualsiasi attributo Qualsiasi nodo di ogni tipo
I libri di ioPROGRAMMO/Programmare con Ajax
63
PROGRAMMARE
CON
Capitolo 3
Esempi:
path expression
/biblioteca/* /biblioteca/reparto/@*
Risultato
Seleziona tutti gli elementi sotto a <biblioteca> indipendentemente dal nome Seleziona tutti gli attributi di <reparto> sotto a <biblioteca> indipendentemente dal nome
Esempi:
path expression
//libro/titolo | //libro/autore
Risultato
Seleziona sia <titolo> che <autore> sotto a <libro>
Internet Explorer usa il motore XPath integrato nella stessa libreria di DOM XML, in questultimo ogni nodo di tipo document o di tipo element ha disposizione due metodi : G selectNodes per selezionare, data unespressione XPath, una nodeList. G selectSingleNode per selezionare, data unespressione XPath,
64
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 3
AJAX
un singolo nodo. Per selezionare un nodo <persona> il cui attributo nome sia uguale a Mario dal file data.xml che raccoglieva la lista di indirizzi che abbiamo visto in precedenza in Internet Explorer sufficiente:
function selezionaIE(docPath){ var objDOM = new ActiveXObject("Msxml2.DOMDocument"); objDOM.load(docPath); var node = objDOM.selectSingleNode("//persona[@nome='Mario']"); return node; }
Firefox
In Firefox le cose sono ben diverse, XPath richiede un Namespace Resolver e una serie di altri parametri, poi occorrer implementare la funzione come callback. Questo il codice per la selezione di una nodeList :
var xmlDoc=null; function apriDomFF(docPath){
I libri di ioPROGRAMMO/Programmare con Ajax
65
PROGRAMMARE
CON
Capitolo 3
xmlDoc = document.implementation.createDocument("", "", null) xmlDoc.onload = _loaded; xmlDoc.load(docPath); } function _loaded(){ var NSResolver = xmlDoc.createNSResolver(xmlDoc.documentElement); var oResult = xmlDoc.evaluate("//persona", xmlDoc, NSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for(i=0; i< oResult.snapshotLength; i++) { var node = oResult.snapshotItem(i); //fai qualcosa con il nodo } }
3.5 XMLHTTPREQUEST
Lultimo (ma forse il pi importante) tra gli strumenti per AJAX che andiamo ad analizzare XMLHttpRequest ovvero quelloggetto che consente di inviare una richiesta (request) HTTP al server e ricevere una risposta (response). A prima vista XMLHttpRequest sembrerebbe un doppione dei vari metodi per caricare un documento XML che abbiamo gi visto parlando del DOM. Si potrebbe pensare infatti che per ricevere un documento XML sia in fondo sufficiente invocare il metodo load dellimplementazione DOM del browser. In realt le cose stanno in modo ben diverso, il load del DOM infatti : 1. effettua solo richieste http di tipo GET e non POST
66
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 3
AJAX
2. non consente (o lo consente in maniera troppo limitata) linvio di parametri al server In sostanza effettuare il load di un documento XML dal server (anche se questultimo fosse generato dinamicamente) consente solamente un flusso unidirezionale server/client, il client non invia informazioni al server ma si limita a riceverle. In qualche caso questo pu essere sufficiente, ma in applicazioni AJAX interattive tipicamente il client a condizionare (in base ai parametri che invia nella request ) le informazioni che poi riceve. Inoltre il metodo POST non soffre delle limitazioni alla lunghezza imposte invece al metodo GET e quindi risulta pi utile per inviare una grande quantit di informazioni. Per supportare AJAX ci serve qualcosa di pi robusto e affidabile del semplice load di XML e XMLHttpRequest fa appunto al caso nostro. Anche loggetto XMLHttpRequest come DOM e XPath deriva da uno standard definito dal W3C quindi i metodi e le propriet che espone sono gli stessi, e cio:
Propriet
onreadystatechange readyState responseText responseXML status statusText Associa un gestore di evento da chiamare quando cambia la propriet readyState Codice numerico che rappresenta lo stato della richiesta. Risposta del server in forma di stringa. Risposta del server in forma di oggetto DOM XML. Il codice numerico di status HTTP ricevuto dal server. Descrizione dello status HTTP ricevuto dal server.
67
PROGRAMMARE
CON
Capitolo 3
Metodi
abort getAllResponseHeaders getResponseHeader open Cancella la richiesta HTTP in corso. Recupera i valori di tutti gli headers HTTP. Recupera il valore di un header HTTP dalla risposta. Inizializza una richiesta specificando il metodo, la URL e la modalit (sincrona o asincrona) della richiesta. Invia una richiesta HTTP al server (con i parametri) e riceve una risposta. Specifica un header HTTP.
send setRequestHeader
Il flusso di una richiesta sincrona con XMLHttpRequest : G creare un istanza delloggetto XMLHttpRequest G invocare il metodo open con i parametri metodo, url e il valore false per qualificare le richiesta come sincrona G se la richiesta di tipo POST utilizzare setRequestHeader per impostare lheader HTTP Content-Type sul valore "application/xwww-form-urlencoded" G invocare il metodo send con il parametro body che contiene i valori da inviare al server G recuperare la risposta con la propriet responseText o responseXML Il flusso di una richiesta asincrona con XMLHttpRequest : G creare un istanza delloggetto XMLHttpRequest G assegnare una funzione di callback attraverso la propriet onreadystatechange G invocare il metodo open con i parametri metodo, url e il valore false per qualificare le richiesta come sincrona G se la richiesta di tipo POST utilizzare setRequestHeader per impostare lheader HTTP Content-Type sul valore "application/xwww-form-urlencoded" G invocare il metodo send con il parametro body che contiene i valori da inviare al server
68
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 3
AJAX
la funzione di callback verr chiamata ad ogni cambiamento di stato della richiesta, allinterno di questa funzione ci dovr essere il controllo della propriet readyState ed il recupero, una volta accertato che readyState sia 4 (completed), della risposta con la propriet responseText o responseXML
Non solo XML Una cosa importante da notare che XMLHttpRequest pu essere utilizzato per ottenere dal server non solo XML well-formed, ma qualunque tipo di file di testo : HTML, testo ASCII ecc
I codici di status HTTP e i relativi valori di statusText (almeno quelli pi probabili) che possiamo ricevere dal server sono:
status Success
200 201 202 203 204 205 206 207
statusText
OK Created Accepted Non-Authoritative Information No Content Reset Content Partial Content Multi-Status
I libri di ioPROGRAMMO/Programmare con Ajax
69
PROGRAMMARE
CON
Capitolo 3
status Redirection
400 401 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
statusText
Bad Request Unauthorized Forbidden Not Found Method Not Allowed Not Acceptable Proxy Authentication Required Request Timeout Conflict Gone Length Required Precondition Failed Request Entity Too Large Request-URI Too Long Unsupported Media Type Requested Range Not Satisfiable Expectation Failed Internal Server Error Not Implemented Bad Gateway Service Unavailable Gateway Timeout HTTP Version Not Supported Bandwidth Limit Exceeded
Errors
500 501 502 503 504 505 509
Nota importante Le versioni attuali di XMLHttpRequest non consentono di connettersi a domini differenti rispetto a quello dove si trova lo script che instanzia loggetto, per cui se lo script si trova, ad esempio, in http://www.mioserver.com/client.html potr richiedere dati solo ad una pagina o procedura che risiede nello stesso dominio (come http://www.mioserver.com/server.aspx ) e non in altri (come http://www.altroserver.com/server.aspx). Si afferma che questa limitazione sia dovuta a motivi di sicurezza
70
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 3
AJAX
(anche se non si ben capito quali possano essere). Se quella di connettersi a server esterni fosse proprio unesigenza indispensabile (come il caso in cui sia necessario connettersi a un Web Service esterno) lunica strada quella di creare una pagina con un linguaggio lato server che faccia da proxy: accetti cio la richiesta dal client, la rigiri al server effettivo, recepisca la risposta e la reindirizzi nuovamente al client. Un esempio di questa tecnica in PHP illustrato su http://developer.yahoo.com/javascript/howto-proxy.html .
Una volta instanziata una XMLHttpRequest il funzionamento identico per tutti i browser. Vediamo quindi come gestire una richiesta sincrona in POST:
I libri di ioPROGRAMMO/Programmare con Ajax
71
PROGRAMMARE
CON
Capitolo 3
function getXMLHttpRequest (){ if (typeof(XMLHttpRequest) != 'undefined') { return new XMLHttpRequest(); } else { return new ActiveXObject("Msxml2.XMLHTTP"); } } function sendSync (){ var req = getXMLHttpRequest(); req.open(POST, data.xml, false); req.setRequestHeader ("Content-Type", "application/x-www-formurlencoded"); req.send(); return req.responseText; }
e qui invece abbiamo una richiesta asincrona (la funzione getXMLHttpRequest sar la stessa dellesempio precedente):
function sendAsync () { var req = getXMLHttpRequest(); var onreadystate = function (){ if (req.readyState == 4) { if (req.status == 200) { var risposta = req.responseText; //fare qualcosa con la risposta } else { var error = req.statusText; //si verificato un errore }
72
PROGRAMMARE CON
Capitolo 3
AJAX
} else { //in attesa } } req.onreadystatechange = onreadystate; //assegnazione al callback interno req.open(POST, data.xml, true); req.setRequestHeader ("Content-Type", "application/x-www-form-urlencoded"); req.send(); }
si noti come abbiamo mantenuta compatta la funzione inserendo il callback come funzione interna.
Invio di parametri al server Negli esempi precedenti nel metodo send abbiamo indicato una stringa vuota, questo perch si trattava semplicemente di scaricare un documento statico (il file data.xml), tuttavia in applicazioni AJAX la fonte XML sar probabilmente generata dinamicamente dal server e quindi avremo bisogno di passargli dei parametri (ad esempio per inviargli i dati di una form) questi parametri devono essere concatenati in una stringa simile a quella che viene usata per le URL e cio:
<nome>=<valore>&<altro_nome>=<altro_valore>
73
PROGRAMMARE
CON
Capitolo 3
Nel caso di variabili conviene per codificare i valori con encodeURIComponent per evitare caratteri riservati :
function sendData (nome,cognome,indirizzo) { ... var params = "nome=" + encodeURIComponent(nome) + "&cognome=" + encodeURIComponent(cognome) + "&indirizzo=" + encodeURIComponent(indirizzo); req.send(params); }
74
PROGRAMMARE CON
Capitolo 3
AJAX
in tal modo tutte le funzioni contenute nella libreria vengono rese disponibili agli script della pagina. Potete provare anche voi a fare la vostra libreria, scrivete del codice in un file che chiamerete ad esempio libreria.js (tipicamente il file di libreria ha estensione .js ma questa solo una convenzione tacita, non una regola), ad esempio:
function metodoLibreria(messaggio){ alert(messaggio) }
Ricordate, nei file di libreria non devono essere inclusi i TAG <script>, solo il codice javascript. Nella pagina HTML di destinazione sar quindi possibile fare riferimento alla libreria:
<html> <head> <script language="javascript" type="text/javascript" src="libreria.js"></script> </head> <body></body> </html>
a questo punto qualsiasi script della pagina potr utilizzare la funzione metodoLibreria :
<html> <head> <script language="javascript" type="text/javascript" src="libreria.js"></script>
I libri di ioPROGRAMMO/Programmare con Ajax
75
PROGRAMMARE
CON
Capitolo 3
<script language="javascript" type="text/javascript"> metodoLibreria ("Ciao a tutti!"); </script> </head> <body> </body> </html> cio sarebbe stato un po come scrivere: <html> <head> <script language="javascript" type="text/javascript"> function metodoLibreria(messaggio){ alert(messaggio) } </script> <script language="javascript" type="text/javascript"> metodoLibreria ("Ciao a tutti!"); </script> </head> <body> </body> </html>
per chi pratico di programmazione web lato server un po come quando si usa una direttiva include per far riferimento a codice che comune a pi pagine.
PROGRAMMARE CON
Capitolo 3
AJAX
Per essere ancora pi chiari: abbiamo visto che listanza di un documento DOM si crea con:
Internet Explorer
var objDOM = new ActiveXObject("Msxml2.DOMDocument");
Firefox
var objDOM = document.implementation.createDocument("", "", null)
Una libreria wrapper offrir quindi un metodo unico per creare unistanza DOM, tipo:
var objDOM = libreria.getDomDocument();
naturalmente la libreria svilupper, al suo interno una funzione getDomDocument che potrebbe essere (semplificando molto) un po come questa:
libreria.getDomDocument = function (){ if(isIE) { return new ActiveXObject("Msxml2.DOMDocument"); } else { return document.implementation.createDocument("", "", null); } }
cio in pratica effettua il riconoscimento del browser e restituisce loggetto creato con il metodo nativo. Il vantaggio di questa tecnica evidente: poter utilizzare metodi univoci senza preoccuparsi dellimplementazione del browser sottoI libri di ioPROGRAMMO/Programmare con Ajax
77
PROGRAMMARE
CON
Capitolo 3
stante.
Sarissa Una delle pi diffuse librerie wrapper Sarissa (il nome deriverebbe da una lunga lancia usata dalle falangi Macedoni) che scaricabile allindirizzo https://sourceforge.net/projects/sarissa . Questa libreria offre appunto una serie di funzioni per XML che si sostituiscono a quelle del browser. Vediamo quali.
Creazione di unistanza DOM XML
possibile chiamare questa funzione anche con due parametri che rappresentano il default namespace e il nome dellelemento radice:
var objDom = Sarissa.getDomDocument("http://xml.org/test","root"); creando cos in memoria un documento XML tipo: <root xmlns="http://xml.org/test"></root>
78
PROGRAMMARE CON
Capitolo 3
AJAX
possibile effettuare il caricamento sincrono o asincrono del file sebbene sia disponibile anche il metodo load per DOM XML che emula quello di Microsoft esso deprecato dagli autori e potrebbe non essere presente nelle future versioni, essi consigliano di usare XMLHttpRequest. In modo sincrono sar quindi:
var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", "data.xml", false); xmlhttp.send(''); var objDom = xmlhttp.responseXML; e in modo asincrono: function loadDoc(){ var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", "data.xml", true); xmlhttp.onreadystatechange = myHandler; xmlhttp.send(''); function myHandler(){ if(xmlhttp.readyState != 4) return ; var objDom = xmlhttp.responseXML; } }
A volte pu essere necessario, soprattutto in fase di debug, controllare i dati ricevuti e trasformare quindi il documento XML in una stringa, magari da visualizzare con unalert. Per svolgere questo compito Sarissa prevede :
var strXml = new XMLSerializer().serializeToString(objDom)
I libri di ioPROGRAMMO/Programmare con Ajax
79
PROGRAMMARE
CON
Capitolo 3
Per XPath Sarissa ricalca il modello usato da Microsoft, abilitando i metodi selectNodes e selectSingleNode anche per gli altri browser, per cui la ricerca si presenta semplice:
... var objDom = xmlhttp.responseXML; var list = objDom.selectNodes ("//persona"); s = ""; for(var i=0;i<list.length;i++){ s += list[i].getAttribute("nome") + "\r\n"; } alert(s);
jsXML Sarissa una bellissima libreria, con il vantaggio di essere anche molto diffusa, ci sono per alcuni svantaggi: G Alcuni metodi (vedi il caricamento di file esterni) sono un po prolissi e quindi richiederebbero ulteriori funzioni per semplificarne lutilizzo. G Linsieme del gruppo di librerie di Sarissa ha una dimensione di circa 60Kb, il che non molto in senso assoluto, tuttavia pu diventare un problema in applicazioni che gi hanno altre librerie da includere; bisogna infatti ricordarsi che anche i file di libreria, come i CSS, le immagini ecc contribuiscono a far lievitare il peso della pagina che il client deve scaricare portando facilmente una pagina a pesare 200/300 Kb, ricordatevi che non tutti hanno lADSL Queste considerazioni mi hanno portato a sviluppare una libreria personale per AJAX che ho chiamato jsXML, scaricabile dal sito: http://www.smelzo.it . I principi su cui si basa questa libreria sono: G Semplificare il pi possibile le cose
80
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 3
AJAX
Ridurre le dimensioni (attualmente 11Kb) Per arrivare alla semplificazione si scelto la strada di emulare, anche per Firefox, il modello proposto da Microsoft, vero che questultimo non il massimo di aderenza agli standard del W3C tuttavia lo ritengo estremamente pi intuitivo e sintetico. Sempre nellottica della semplificazione, si sono implementati dei metodi che consentono di fare delle operazioni con una sola riga di codice. Alla riduzione (ad un sesto di Sarissa) si arrivati invece con la scelta di limitarsi alla compatibilit con Internet Explorer e Firefox (molte funzioni lavorano tuttavia correttamente anche in Opera); questo pu anche sembrare poco politically correct tuttavia c da considerare IE e Firefox insieme coprono ormai quasi il 98% del mercato e praticamente tutti i sistemi operativi e che le applicazioni AJAX vengono usate soprattutto in parti del sito controllate come forum, amministrazione ecc dove cio si ha un certo potere di indirizzamento verso luso di un certo browser. Vediamo quindi come compiere le pi comuni operazioni con XML con jsXML.
Creazione di unistanza DOM XML
Anche qui si semplificano le cose riunendo creazione di unistanza e caricamento da stringa in ununica funzione:
var objDom= jsXML.parseXMLDocument(strXml);
dove il parametro sar ovviamente la stringa XML su cui costruire il nuovo documento
I libri di ioPROGRAMMO/Programmare con Ajax
81
PROGRAMMARE
CON
Capitolo 3
Gestione XMLHttpRequest
Essendo unoperazione frequentissima in AJAX le operazioni XMLHttpRequest sono completamente gestite da funzioni di utilit. Per una richiesta sincrona :
var strXML = jsXML.sendSync (data.xml,param1=X¶m2=Y);
in questo, come in altri casi la richiesta d una risposta in forma di stringa e non di XML per permettere di gestire anche risorse non XML. Il primo parametro lURL della risorsa e il secondo sono i valori da inviare eventualmente al server , di default la richiesta effettuata con POST ma si pu utilizzare anche un altro metodo specificandolo come terzo parametro:
var strXML = jsXML.sendSync (data.xml,param1=X¶m2=Y,GET);
Nel caso che invece di una risposta come stringa si desideri ottenere direttamente una risposta come oggetto DOM XML sufficiente usare jsXML.direct:
var objDOM = jsXML.direct (data.xml,param1=X¶m2=Y);
82
PROGRAMMARE CON
Capitolo 3
AJAX
function errorHandler (statusText) { // fai qualcosa con statusText } //callback waitHandler function errorHandler (readyState) { // fai qualcosa con readyState }
come avrete intuito, il primo parametro il metodo utilizzato, il secondo e il terzo sono URL e valori da inviare e i restanti tre sono riferimenti a funzioni di callback : la prima viene invocata in caso di successo e riceve come parametro la risposta come stringa XML, la seconda viene invocata in caso di errore e riceve come parametro il messaggio di errore http, lultima viene invocata mentre si attende la risposta e pu essere utile per mostrare un messaggio di attesa. Dei tre callback obbligatorio solo il primo. Per semplificare ulteriormente si hanno anche altre due funzioni satellite che indirizzano direttamente verso la chiamata GET o POST eliminando il primo parametro:
jsXML.sendAsyncGet (data.xml,param1=X¶m2=Y, successHandler,errorHandler,waitHandler);
e
jsXML.sendAsyncPost (data.xml,param1=X¶m2=Y, successHandler,errorHandler,waitHandler);
83
PROGRAMMARE
CON
Capitolo 3
Anche qui si seguita la strada di Microsoft per cui per ogni nodo documento o elemento sufficiente richiamare la propriet xml:
var strXML = objDom.xml; //per lintero documento var strXML = objDom.documentElement.xml; //per un altro nodo
Come in Sarissa si sono collegate le query XPath ai metodi selectNodes e selectSingleNode e quindi:
var objDom= jsXML.newDOMDocument(); objDom.load (data.xml); var list = objDom.selectNodes ("//persona"); s = ""; for(var i=0;i<list.length;i++){ s += list[i].getAttribute("nome") + "\r\n"; } alert(s);
google-ajaxslt Unaltra libreria per AJAX google-ajaxslt (http://code.google.com/p/ajaxslt ), la citiamo pi per lapproccio particolare che adotta che per la reale praticabilit. Il progetto unimplementazione di XPath e XSLT fatta completamente in javascript! Cio unimpresa a dir poco titanica. La scelta dei progettisti di questa libreria quella di non appoggiarsi per nulla alle implementazioni native di DOM XML, XPath e XSLT
84
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 3
AJAX
ma di riscriverli da zero (e per giunta in javascript!) eliminando cos alla radice il problema delle differenze tra i vari browser. Agli scopi pratici la libreria non ancora ad uno stadio maturo, un po perch mancano una serie di funzioni indispensabili, un po perch pi orientata a XSLT che in AJAX pu s avere un ruolo, ma non proprio di primo piano. Lidea infatti sarebbe di trasformare linput XML ricevuto dal server, attraverso XSLT, in codice HTML, questo approccio pu andare bene per del contenuto statico, ma se nella fase intermedia tra acquisizione e creazione del contenuto si dovessero impostare elementi HTML con gestori di eventi questo approccio non andrebbe pi bene. Comunque il progetto veramente notevole per lo stile e labilit di scrittura, una vera miniera di spunti; i programmatori un po pi esperti di javascript farebbero bene a dare almeno unocchiata ai sorgenti.
Microsoft AJAX Library Tuttaltra strada quella intrapresa dalla Microsoft AJAX Library, la libreria javascript client-side che parte del pi ampio progetto ASP.NET AJAX (http://ajax.asp.net ). Sebbene sia concepita per essere la parte client di un sistema che prevede lintegrazione di alcuni nuovi Web Controls per ASP.NET (ne parliamo pi avanti nel capitolo Finalmente AJAX), la libreria pu essere utilizzata anche autonomamente da ASP.NET cio anche con altri linguaggi. La sua filosofia un po quella di Sarissa, ovvero creare un wrapper delle funzioni native per XML dei vari browser in circolazione, tuttavia per farlo mette in piedi una gigantesca API dove c addirittura un tentativo di tipizzazione di un linguaggio di scripting, per natura non tipizzato, come javascript. Il risultato indubbiamente molto Object Oriented, tuttavia se lo scopo era quello di semplificare le cose sembra proprio che siamo lontani dallobbiettivo.
I libri di ioPROGRAMMO/Programmare con Ajax
85
PROGRAMMARE
CON
Capitolo 3
Il lato positivo di Microsoft AJAX Library che funziona anche con Opera e Safari, ma allora secondo me conviene usare Sarissa che ha unAPI decisamente pi intuitiva.
In un editor di testo creiamo la pagina HTML che abbiamo utilizzato in precedenza, ma con laggiunta di un riferimento ad una libreria di funzioni per XML (in questo caso jsXML) :
<html> <head> <title>Esempio</title> <script language="javascript" type="text/javascript" src="jsXML.js"></script> <script language="javascript" type="text/javascript"> //codice javascript
86
PROGRAMMARE CON
Capitolo 3
AJAX
</script> </head> <body> <button id="btn1" onclick="cliccami()">Clicca qui per vedere la tabella!</button> <div id="container" style="display:none"></div> </body> </html>
Questa la funzione che subisce maggiori cambiamenti perch questa volta la sorgente dei dati un file XML che dovr essere caricato e letto con XPath:
function createTable(){ //definizione dati tabella //prima riga var headers = ["nome","cognome","indirizzo"]; var objDOM = jsXML.openDOMDocument("data.xml"); var nodes = objDOM.selectNodes("//persona"); var oTable = document.createElement("TABLE"); //propriet tabella oTable.width = "400"; oTable.cellSpacing = "0"; oTable.cellPadding = "2"; //prima riga var oTr = oTable.insertRow(-1); for(var i=0;i<headers.length;i++){ var oTd = oTr.insertCell(-1); oTd.align="center"; oTd.background = "#EEEEEE"; oTd.style.font = "bold 12px arial"; oTd.style.borderBottom = "1px solid #666666";
I libri di ioPROGRAMMO/Programmare con Ajax
87
PROGRAMMARE
CON
Capitolo 3
oTd.innerHTML= headers[i]; } //righe dati for(var i=0;i<nodes.length;i++){ var node = nodes[i]; var oTr = oTable.insertRow(-1); for(var n=0;n<headers.length;n++){ var oTd = oTr.insertCell(-1); oTd.style.font = "12px verdana"; oTd.style.borderBottom = "1px solid #999999"; oTd.innerHTML= node.getAttribute(headers[n]) ; } } currentTable = oTable; var container = document.getElementById("container"); container.appendChild(currentTable); }
in pratica : G apriamo il documento XML G estraiamo con XPath i nodi <persona> G nel ciclo di costruzione della tabella leggiamo i vari attributi con getAttribute le altre parti del programma sono uguali a quanto visto nel primo capitolo e anche il risultato il medesimo.
88
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
LATO SERVER
Dopo aver trattato il pi diffusamente possibile le problematiche clientside legate a javascript, HTML DOM e XML, dobbiamo adesso prendere in esame laltro importante attore di AJAX: il lato server. Infatti, pur ridotto nellimportanza rispetto alle Web Applications tradizionali, il server continua ad avere una funzione basilare : quella di sorgente di dati. In AJAX, come abbiamo detto, abbiamo un colloquio ben definito tra gli script che ci sono nella pagina, un ciclo molto simile per certi aspetti a quello delle applicazioni client/server tradizionali: 1. clientserver richiesta dati 2. serverclient invio dati 3. client elaborazione dati e cambiamenti allinterfaccia utente Da quanto abbiamo visto finora con XMLHttpRequest dovremo essere in grado di inviare una richiesta al server e quindi assolvere al punto 1. Ma siamo anche in grado di manipolare adeguatamente con javascript sia un documento XML (con il DOM e XPath) che loutput HTML (con il DOM HTML e i CSS) e quindi siamo pronti anche per il punto 3. Quello che ci manca quindi soltanto il punto 2 : costruire una risposta dal lato server (che, ovviamente, dovr essere un flusso XML). Il 99% delle operazioni che restano da fare al server riguardano i dati (che siano database, file XML o file di testo non ha importanza) e sono, tipicamente: G selezione G inserimento G modifica G cancellazione Ogni sviluppatore avr naturalmente il suo linguaggio preferito per compiere queste operazioni lato server, noi partiremo comunque da un problema specifico da risolvere per vedere come implementare soluzioni in diversi linguaggi, questo naturalmente solo un esempio, in applicazioni
I libri di ioPROGRAMMO/Programmare con Ajax
89
PROGRAMMARE
CON
Capitolo 4
reali le cose potrebbero essere pi complesse, tuttavia negli esempi che faremo cercheremo di toccare le problematiche principali.
4.1 IL PROBLEMA
Abbiamo un database, il buon vecchio Northwind il database di esempio della Microsoft, che si trova in tutte le edizioni di Access e anche in qualche distribuzione di SQL Server; per collegarsi a Northwind utilizzeremo ODBC (in modo da mantenersi il pi neutrali possibile rispetto al tipo di database di origine), vogliamo selezionare i dati dalla tabella Customers (clienti). Questa selezione sar influenzata, nella quantit di dati restituiti, dallinput dellutente che potr: G impostare una stringa di ricerca G impostare il campo dove effettuare la ricerca Si tratta quindi di predisporre una pagina server-side che si connetta al Database ODBC, trasmetta la query costruita in base alle impostazioni dellutente, recuperi i dati e li trasmetta come response XML. La connessione ODBC avr come nome NWIND.
90
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
$Query = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers "; //recupero dati $queryexe = odbc_do($connectionstring, $Query); //lettura dati while(odbc_fetch_row($queryexe)) { //lettura campi $cname = odbc_result($queryexe, "CompanyName"); ... } //disconnessione dal database odbc_close($connectionstring);
quindi : G connessione G impostazione query G esecuzione query G lettura di righe e campi G disconnessione Fin qui tutto bene, ma bisogna ricordarsi che loutput dovr essere in XML e quindi dovremo impostare lHeader di risposta con il Content-Type appropriato ovvero :
header('Content-type: text/xml');
Costruire la query C poi da gestire la query impostando la selezione in base dellinput dellutente. Partiamo proprio dallinput dellutente, per prima cosa dovremo intercettare le variabili Request che potranno essere :
I libri di ioPROGRAMMO/Programmare con Ajax
91
PROGRAMMARE
CON
Capitolo 4
G search la stringa da cercare G field il campo in cui effettuare la ricerca Prevederemo anche il caso in cui lutente non imposti il campo valorizzando la variabile con il nome del primo campo della query. Il tutto si traduce nel codice:
cio : se presente limpostazione della variabile $search ripuliamo la stringa dal carattere ' che darebbe luogo a errori SQL sostituendolo con i doppi apici; costruiamo poi la clausola WHERE accodandola alla stringa $Query.
Costruire la risposta XML LXML sar costruito allinterno del ciclo di lettura dei dati come stringa e restituito con echo:
$s = "<root>"; //lettura dati while(odbc_fetch_row($queryexe)) { $s .= "<persona";
92
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
$cname = enc(odbc_result($queryexe, "CompanyName")); $pname = enc(odbc_result($queryexe, "ContactName")); $address = enc(odbc_result($queryexe, "Address") . " " . odbc_result($queryexe, "City") . " " . odbc_result($queryexe, "Country") ); $s .= " company=\"$cname\""; $s .= " contact=\"$pname\""; $s .= " address=\"$address\"/>"; } $s .= "</root>"; //disconnessione dal database odbc_close($connectionstring); echo($s);
avrete senzaltro notato che il risultato della lettura del campo, effettuato con la funzione odbc_result, stato passato come parametro alla funzione enc? Abbiamo fatto in questo modo perch nella stringa proveniente dal database ci potrebbero essere dei caratteri speciali che XML potrebbe segnalare come errori. La funzione enc si occupa appunto di codificare i caratteri della stringa in formato comprensibile per XML:
function enc($s){ return xmlentities($s); }
La funzione di appoggia a sua volta alla funzione xmlentities (presa pari pari da internet su http://theserverpages.com/php/manual/en/function.htmlentities.php ) che espressa cos:
function xmlentities($string, $quote_style=ENT_QUOTES){ static $trans;
I libri di ioPROGRAMMO/Programmare con Ajax
93
PROGRAMMARE
CON
Capitolo 4
if (!isset($trans)) { $trans = get_html_translation_table(HTML_ENTITIES, $quote_style); foreach ($trans as $key => $value) $trans[$key] = '&#'.ord($key).';'; $trans[chr(38)] = '&'; } return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/","&" , strtr($string, $trans)); }
94
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
//lettura dati while(odbc_fetch_row($queryexe)) { $s .= "<persona"; $cname = enc(odbc_result($queryexe, "CompanyName")); $pname = enc(odbc_result($queryexe, "ContactName")); $address = enc(odbc_result($queryexe, "Address") . " " . odbc_result($queryexe, "City") . " " . odbc_result($queryexe, "Country") ); $s .= " company=\"$cname\""; $s .= " contact=\"$pname\""; $s .= " address=\"$address\"/>"; } $s .= "</root>"; //disconnessione dal database odbc_close($connectionstring); echo($s); /*funzioni di encode XML*/ function xmlentities($string, $quote_style=ENT_QUOTES) { static $trans; if (!isset($trans)) { $trans = get_html_translation_table(HTML_ENTITIES, $quote_style); foreach ($trans as $key => $value) $trans[$key] = '&#'.ord($key).';'; $trans[chr(38)] = '&'; } return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/","&" , strtr($string, $trans)); } function enc($s){ return xmlentities($s); } ?>
I libri di ioPROGRAMMO/Programmare con Ajax
95
PROGRAMMARE
CON
Capitolo 4
Puntiamo quindi il browser sulla pagina in questione e otterremo lelenco completo della query sulla tabella in formato XML (vedi figura 3).
A questo punto potremo sbizzarrirci con le prove di selezione impostando nella URL i vari parametri di selezione; ad esempio scrivendo la URL come
http://mioserver/server.php?search=b&field=contactName
96
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
Avremo sul browser il risultato di cui in figura 4 (ovviamente la parte di URL http://mioserver andr sostituita con lindirizzo del server in cui avrete pubblicato la pagina)
97
PROGRAMMARE
CON
Capitolo 4
Costruire la query Valutiamo linput dellutente contenuto nelle variabili search e field e costruiamo la query di selezione :
Dim search,field search = Request("search") field = Request("field") if field="" then field = "CompanyName" ... Query = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers " if search<>"" then search = replace(search,"'","''") Query = Query & " WHERE " & field & " LIKE '" & search & "%'" end if
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
come possiamo notare SAX scrive direttamente sullo stream Response di ASP
Il risultato Il risultato finale del nostro lavoro in classic ASP sar quindi:
99
PROGRAMMARE
CON
Capitolo 4
<% Response.ContentType= "text/xml" Dim search,field Dim connString,Query search = Request("search") field = Request("field") if field="" then field = "CompanyName" connString ="DSN=NWIND;Uid=fs;Pwd=fs;" Dim conn , rs set conn=CreateObject("ADODB.CONNECTION") conn.ConnectionString=connString set rs = CreateObject("ADODB.RECORDSET") Query = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers " if search<>"" then search = replace(search,"'","''") Query = Query & " WHERE " & field & " LIKE '" & search & "%'" end if conn.Open rs.Open Query,conn 'Istanza di xmlWriter Set xmlWriter = Server.CreateObject("MSXML2.MXXMLWriter.4.0") xmlWriter.omitXMLDeclaration = True xmlWriter.indent = false xmlWriter.encoding="UTF-8" 'invia output del writer al response stream di ASP xmlWriter.output = Response 'Genera eventi SAX xmlWriter.startDocument xmlWriter.startElement "", "", "root", nothing Set objAttributes = Server.CreateObject("Msxml2.SAXAttributes.4.0")
100
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
do until rs.EOF cname = rs("CompanyName") pname = rs("ContactName") address = rs("Address") & " " & _ rs("City") & " " & _ rs("Country") objAttributes.addAttribute "","","company","CDATA",CStr(cname) objAttributes.addAttribute "","","contact","CDATA",CStr(pname) objAttributes.addAttribute "","","address","CDATA",CStr(address) xmlWriter.startElement "", "", "persona", objAttributes xmlWriter.endElement "", "", "persona" objAttributes.clear rs.MoveNext loop xmlWriter.endElement "", "", "root" xmlWriter.endDocument rs.Close conn.Close set rs = nothing set conn = nothing set objAttributes = nothing set xmlWriter= nothing %>
Puntiamo il nostro browser sulla pagina server.asp del sito di test e otterremo come risultato lo stream XML della selezione effettuata (in maniera identica a quanto avveniva con PHP). Anche qui naturalmente potremmo provare varie selezioni impostando i parametri della queryString della URL come ad esempio:
http://mioserver/server.asp?search=b&field=contactName
101
PROGRAMMARE
CON
Capitolo 4
In C# possiamo modulare meglio il flusso del programma per cui impostiamo delle propriet (Search e Field) che raccolgono i criteri di ricerca impostati dallutente.
public string Search { get
102
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
{ string s = Request.Params.Get("search"); if (!String.IsNullOrEmpty(s)) s = s.Replace("'", "''"); return s; } } public string Field { get { string f = Request.Params.Get("field"); if (String.IsNullOrEmpty(f)) f = "CompanyName"; return f; } }
com possibile notare la gestione dei valori nulli e dei caratteri non validi avviene direttamente allinterno delle propriet Impostiamo quindi la funzione che compone la query al database:
private string GetQuery() { string query = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers "; if (Search != null) { query += String.Format(" WHERE {0} LIKE '{1}%'", Field, Search); } return query; }
103
PROGRAMMARE
CON
Capitolo 4
Creazione dellXML Anche qui, come abbiamo fatto in classic ASP, utilizziamo un XmlWriter per scrivere i dati che provengono dal database, soltanto che in .NET loggetto molto pi semplice:
XmlTextWriter writer = new XmlTextWriter(this.Response.Output); writer.WriteStartDocument(); writer.WriteStartElement("root"); while (reader.Read()) { string cname = reader.GetString(0); string pname = reader.GetString(1); string address = reader.GetString(2) + " " + reader.GetString(3) + rea der.GetString(4); writer.WriteStartElement("persona"); writer.WriteAttributeString("company", cname); writer.WriteAttributeString("contact", pname); writer.WriteAttributeString("address", address); writer.WriteEndElement(); } reader.Close(); writer.WriteEndElement(); writer.WriteEndDocument(); writer.Close();
da notare che abbiamo puntato loutput dellXmlWriter direttamente sul flusso della Response della pagina.
104
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
Il recupero dei dati, come in parte abbiamo visto, avviene per mezzo di un DataReader creato da un Command che esegue la query:
OdbcConnection cnn = this.GetConn(); OdbcCommand cmd = cnn.CreateCommand(); cmd.CommandType = CommandType.Text; cmd.CommandText = this.GetQuery(); cnn.Open(); OdbcDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
Il risultato Tutto il codice della nostra pagina ASP.NET in C# sar alla fine:
<%@ Page Language="C#" %> <%@ Import Namespace="System" %> <%@ Import Namespace="System.Web" %> <%@ Import Namespace="System.Xml" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.Odbc" %> <script runat="server"> public string Search { get { string s = Request.Params.Get("search"); if (!String.IsNullOrEmpty(s)) s = s.Replace("'", "''"); return s; } } public string Field { get
I libri di ioPROGRAMMO/Programmare con Ajax
105
PROGRAMMARE
CON
Capitolo 4
{ string f = Request.Params.Get("field"); if (String.IsNullOrEmpty(f)) f = "CompanyName"; return f; } } private string GetQuery() { string query = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers "; if (Search != null) { query += String.Format(" WHERE {0} LIKE '{1}%'", Field, Search); } return query; } private OdbcConnection GetConn() { string connStr = "DSN=NWIND;Uid=fs;Pwd=fs;"; return new OdbcConnection(connStr); } protected void Page_Load(object sender, EventArgs e) { this.Response.ContentType = "text/xml"; OdbcConnection cnn = this.GetConn(); OdbcCommand cmd = cnn.CreateCommand(); cmd.CommandType = CommandType.Text; cmd.CommandText = this.GetQuery(); cnn.Open(); OdbcDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); XmlTextWriter writer = new XmlTextWriter(this.Response.Output);
106
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
writer.WriteStartDocument(); writer.WriteStartElement("root"); while (reader.Read()) { string cname = reader.GetString(0); string pname = reader.GetString(1); string address = reader.GetString(2) + " " + reader.GetString(3) + reader.GetString(4); writer.WriteStartElement("persona"); writer.WriteAttributeString("company", cname); writer.WriteAttributeString("contact", pname); writer.WriteAttributeString("address", address); writer.WriteEndElement(); } reader.Close(); writer.WriteEndElement(); writer.WriteEndDocument(); writer.Close(); } </script>
Puntiamo il nostro browser sulla pagina server.aspx del sito di test e otterremo come risultato lo stesso XML degli esempi precedenti. Anche qui naturalmente potremmo provare varie selezioni impostando i parametri della queryString della URL come ad esempio:
http://mioserver/server.aspx?search=a&field=Address
sempre naturalmente sostituendo la parte di URL http://mioserver con lindirizzo del server in cui avrete pubblicato la pagina.
E se preferiamo VB Naturalmente lesempio che abbiamo visto in C# pu essere anche scritI libri di ioPROGRAMMO/Programmare con Ajax
107
PROGRAMMARE
CON
Capitolo 4
to in VB.NET come:
<%@ Import Namespace="System" %> <%@ Import Namespace="System.Web" %> <%@ Import Namespace="System.Xml" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.Odbc" %> <%@ Page Language="VB" %> <script runat="server"> Public ReadOnly Property Search() As String Get Dim s As String = Request.Params.Get("search") If Not String.IsNullOrEmpty(s) Then s = s.Replace("'", "''") Return s End Get End Property Public ReadOnly Property Field() As String Get Dim s As String = Request.Params.Get("field") If Not String.IsNullOrEmpty(s) Then s = "CompanyName" Return s End Get End Property Private Function GetQuery() As String Dim query As String = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers " If Not String.IsNullOrEmpty(Search) Then query &= String.Format(" WHERE {0} LIKE '{1}%'", Field, Search) End If Return query End Function
108
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
Private Function GetConn() As OdbcConnection Dim connStr As String = "DSN=NWIND;Uid=fs;Pwd=fs;" Return New OdbcConnection(connStr) End Function Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Me.Response.ContentType = "text/xml" Dim cnn As OdbcConnection = Me.GetConn() Dim cmd As OdbcCommand = cnn.CreateCommand() cmd.CommandType = CommandType.Text cmd.CommandText = Me.GetQuery() cnn.Open() Dim reader As OdbcDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection) Dim writer As New XmlTextWriter(Me.Response.Output) writer.WriteStartDocument() writer.WriteStartElement("root") While reader.Read Dim cname As String = reader.GetString(0) Dim pname As String = reader.GetString(1) Dim address As String = reader.GetString(2) & " " & reader.GetString(3) & reader.GetString(4) writer.WriteStartElement("persona") writer.WriteAttributeString("company", cname) writer.WriteAttributeString("contact", pname) writer.WriteAttributeString("address", address) writer.WriteEndElement() End While reader.Close() writer.WriteEndElement() writer.WriteEndDocument()
I libri di ioPROGRAMMO/Programmare con Ajax
109
PROGRAMMARE
CON
Capitolo 4
Prima di iniziare dovremmo per definire un file web.config per lapplicazione Web. Il file web.config un semplice file XML posto nella directory root del sito Web e contenente le impostazioni di configurazione. Per i nostri scopi basta impostare il web.config in questo modo :
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <webServices> <conformanceWarnings> <clear/>
110
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
</conformanceWarnings> <protocols> <add name="HttpPost"/> <add name="HttpGet"/> </protocols> </webServices> </system.web> </configuration>
Quello che abbiamo visto serve per : G Eliminare gli avvisi di conformit rispetto ad alcuni standard. Infatti poich utilizziamo il flusso XML prodotto dal Web Service con AJAX, e quindi senza una deserializzazione, sarebbe superfluo gestire i Namespaces come invece richiederebbero gli standard di conformit. G Abilitare lutilizzo del Web Service da POST e/o da GET oltre che da SOAP AJAX attraverso il parsing XML potrebbe anche gestire SOAP, ma questo produrrebbe soltanto output pi grandi, senza alcun beneficio pratico; quindi useremo POST, GET lo consentiamo per effettuare dei test, ma in produzione bene toglierlo
Loutput XML Con un Web Service loutput costituito sempre da XML e quindi non c alcuna necessit di controllare direttamente il flusso. Per trasformare loggetto di risposta in XML il Web Service utilizza la Serializzazione XML, quindi, per modellare la risposta secondo le nostre esigenze dovremo creare un oggetto con gli adeguati attributi di controllo della serializzazione. Noi abbiamo quindi modellato il nostro oggetto in una classe:
<XmlRoot("root", Namespace:="")> _ Public Class Indirizzi Public Sub Add(ByVal company As String, ByVal contact As String, ByVal address As String)
I libri di ioPROGRAMMO/Programmare con Ajax
111
PROGRAMMARE
CON
Capitolo 4
Dim p As New Persona p.company = company p.contact = contact p.address = address Persone.Add(p) End Sub Private _Persone As List(Of Persona) <XmlElement("persona")> _ Public Property Persone() As List(Of Persona) Get If _Persone Is Nothing Then _Persone = New List(Of Persona) End If Return _Persone End Get Set(ByVal Value As List(Of Persona)) _Persone = Value End Set End Property Public Class Persona <XmlAttribute()> Public company As String <XmlAttribute()> Public contact As String <XmlAttribute()> Public address As String End Class End Class
gli attributi <XmlRoot>, <XmlElement>, <XmlAttribute> permettono appunto di definire la formattazione in fase di serializzazione della classe. La struttura del metodo che verr richiamato dal client :
<WebMethod()> _
112
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
Public Function GetSelection(ByVal search As String, ByVal field As String) As Indirizzi ... End Function
lattributo <WebMethod()> indica infatti che il Web Service espone il metodo GetSelection , in fase di risposta loggetto di tipo Indirizzi che risulta dalla funzione sar serializzato in XML secondo le indicazioni che sono state fornite negli attributi della classe Indirizzi che abbiamo visto prima.
Il risultato Il resto del codice molto simile a quanto visto negli esempi precedenti, il sorgente completo si presenta:
<%@ WebService Language="VB" Class="server" %> Imports System Imports System.Web Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.Data Imports System.Data.Odbc Imports System.Collections.Generic Imports System.Xml Imports System.Xml.Serialization <WebService(Namespace:="")> _ Public Class server Inherits System.Web.Services.WebService <WebMethod()> _ Public Function GetSelection(ByVal search As String, ByVal field As String) As Indirizzi
I libri di ioPROGRAMMO/Programmare con Ajax
113
PROGRAMMARE
CON
Capitolo 4
Dim query As String = "SELECT CompanyName, ContactName, Address, City, Country FROM Customers " If Not String.IsNullOrEmpty(search) Then search = search.Replace("'", "''") If String.IsNullOrEmpty(field) Then field = "CompanyName" query &= String.Format(" WHERE {0} LIKE '{1}%'", field, search) End If Dim cnn As New OdbcConnection("DSN=NWIND;Uid=fs;Pwd=fs;") Dim cmd As OdbcCommand = cnn.CreateCommand() cmd.CommandType = CommandType.Text cmd.CommandText = query cnn.Open() Dim reader As OdbcDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection) Dim result As New Indirizzi While (reader.Read()) Dim cname As String = reader.GetString(0) Dim pname As String = reader.GetString(1) Dim address As String = reader.GetString(2) + " " + reader.GetString(3) + reader.GetString(4) result.Add(cname, pname, address) End While Return result End Function <XmlRoot("root", Namespace:="")> _ Public Class Indirizzi Public Sub Add(ByVal company As String, ByVal contact As String, ByVal address As String) Dim p As New Persona p.company = company p.contact = contact p.address = address
114
PROGRAMMARE CON
Capitolo 4
Lato server
AJAX
Persone.Add(p) End Sub Private _Persone As List(Of Persona) <XmlElement("persona")> _ Public Property Persone() As List(Of Persona) Get If _Persone Is Nothing Then _Persone = New List(Of Persona) End If Return _Persone End Get Set(ByVal Value As List(Of Persona)) _Persone = Value End Set End Property Public Class Persona <XmlAttribute()> Public company As String <XmlAttribute()> Public contact As String <XmlAttribute()> Public address As String End Class End Class End Class
Se andiamo ad aprire il browser allindirizzo della nostra pagina server.asmx troveremo uninterfaccia con un modulo che ci consente di testare direttamente il funzionamento del servizio, com possibile vedere in figura 5. Se proviamo ad impostare i parametri e cliccare sul bottone richiama otterremo una risposta XML del tutto analoga a quella che abbiamo visto negli altri esempi, vedi figura 6.
115
PROGRAMMARE
CON
Capitolo 4
116
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
FINALMENTE AJAX
Tutto quanto visto fino adesso ci dovrebbe aver dato sufficiente bagaglio tecnico per affrontare una piccola applicazione in AJAX. Come sempre partiamo da un problema specifico piuttosto che da una trattazione teorica.
5.1 IL PROBLEMA
Vogliamo fare unapplicazione che consenta la ricerca allinterno della tabella Customers del database Northwind, ci sar una textbox dove lutente inserir i termini della ricerca, una select che consente di scegliere il campo in cui effettuare la ricerca e un bottone di invio. Sul click sul bottone di invio dovr partire una XMLHttpRequest verso una delle pagine lato server che abbiamo predisposto, dovremo poi interpretare i risultati e trasformarli in una tabella HTML per presentarli allutente, il tutto senza ovviamente ricaricare la pagina.
5.2 SOLUZIONE
Prima di tutto dovremo creare una nuova pagina HTML. Per dimostrare come AJAX sia totalmente indipendente da tecnologie lato server, scegliamo di creare una pagina in HTML statico alla quale quindi daremo unestensione .html o .htm.
Il layout HTML Inseriamo nella pagina tutti gli elementi di partenza ovvero: G una form contenente: un textBox per inserire la stringa di ricerca un select per selezionare il campo della tabella un bottone submit G un DIV vuoto destinato a contenere i risultati Il layout risultante sar:
<div class="title">Selezione tabella clienti</div>
I libri di ioPROGRAMMO/Programmare con Ajax
117
PROGRAMMARE
CON
Capitolo 5
<form onsubmit="sendRequest();return false"> <table class="layoutTable" cellpadding="2" cellspacing="0"> <tr> <td> <div class="label">Parola iniziale</div> <input class="inputBox" type="text" id="search"> </td> <td> <div class="label">Nel campo</div> <select id="field"></select> </td> <td valign="bottom"> <input type="submit" class="submit" value="Invia"> </td> </tr> </table> </form> <div id="result"></div>
naturalmente avremo cura di attribuire degli id a quegli elementi che saranno coinvolti nel processo AJAX in modo da poterli facilmente referenziare nel codice javascript. Il <form>, come vedete, non punta a nessuna pagina in particolare (manca infatti lattributo action) ma serve solo a gestire levento onsubmit che esegue la funzione sendRequest (che definiremo poi) e quindi blocca linvio effettivo con return false. In realt avremmo potuto anche assegnare direttamente la funzione sendRequest allevento onclick di un bottone senza utilizzare per niente il <form> tuttavia, in questo caso, la funzione non sarebbe stata invocata alla pressione del tasto Return sulla casella di testo e questo sarebbe un comportamento che potrebbe disorientare molti utenti. Unaltra particolarit quella che la <select> che dovrebbe contenere la lista dei nomi dei campi in realt vuota.
118
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
I campi su cui lutente pu basare la selezione sono: G CompanyName G ContactName G Address G City G Country NellXML risultante per i campi (attributi del nodo) sono solo tre : company (da CompanyName) , contact (da ContactName), e address (dalla concatenazione di Address, City e Country) come possiamo vedere da uno degli elementi delloutput:
<persona company="Alfreds Futterkiste" contact="Maria Anders" address="Obere Str. 57 BerlinGermany"/>
Da ci ne ricaviamo che: G tutti i nomi dei campi devono apparire nella selectBox G soltanto alcuni devono apparire nella tabella risultato Quindi, in javascript andiamo a costruire un modello per definire i campi:
var fields = [ {"value":"CompanyName","caption":"Nome azienda", "attname":"company","header":true}, {"value":"ContactName","caption":"Nome contatto",
I libri di ioPROGRAMMO/Programmare con Ajax
119
PROGRAMMARE
CON
Capitolo 5
Cio dichiariamo una variabile fields che contiene un Array di oggetti definiti inplace che hanno le seguenti propriet: G value il nome del campo nella query SQL e valore nella <option> della <select>. G caption il nome convenzionale del campo che useremo per le intestazioni delle colonne e per il testo nella <option> della <select>. G attname il nome dellattributo nel nodo <persona> che ci arriva dal server. Questo presente solo nel caso in cui header sia true. G header indica se utilizzare loggetto per le intestazioni di colonna e per il recupero dati. Certo che abbiamo complicato un po le cose non vero? Ma solo in apparenza, in realt questi oggetti ci saranno molto utili: G per riempire la selectBox G per costruire le intestazioni colonna della tabella G per recuperare i valori da inserire nelle celle della tabella vero, come obbietter qualcuno, che nulla ci vieta di scrivere i valori della selectBox direttamente nei tag <option>, cos:
<select id="field"> <option value="CompanyName">Nome azienda</option> <option value="ContactName">Nome contatto</option> ... </select>
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
dovessimo utilizzare unaltra query o cambiassero i nomi campi. Disponendo invece dellArray di oggetti fields possiamo riempire dinamicamente la selectBox, con questa funzione :
function fillSelectFields(){ //riferimento al <select> var oSel = document.getElementById("field"); for(var i=0;i<fields.length;i++){ var field = fields[i]; //nuovo elemento <option> var oOpt = oSel.appendChild( document.createElement("option") ); oOpt.value = field.value; oOpt.innerHTML= field.caption; } }
Invio della richiesta Linvio della richiesta al server, con la libreria jsXML, una cosa semplice:
function sendRequest(){ var serverUrl = "server.php"; //o server.asp o server.aspx o server.asmx var searchValue = document.getElementById("search").value; var fieldValue = document.getElementById("field").value; //parametri var requestBody = "search=" + encodeURIComponent(searchValue) + "&field=" + encodeURIComponent(fieldValue); jsXML.sendAsyncPost(serverUrl, requestBody, formatResult, error, wait);
I libri di ioPROGRAMMO/Programmare con Ajax
121
PROGRAMMARE
CON
Capitolo 5
In pratica abbiamo indicato nella variabile requestBody i parametri selezionati (che derivano dallinputBox e dalla selectBox) e abbiamo invocato una richiesta asincrona in Post con jsXML.sendAsyncPost. A questo punto la logica si sposta sulle tre funzioni callback: formatResult, error, wait; le ultime due regolano rispettivamente il caso in cui si verifichi un errore o in cui ancora il flusso non sia terminato. Il loro codice sar:
function error (msg){ alert(msg); } function wait(){ document.getElementById("result").innerHTML= "Attendi..." }
ovvero: in caso derrore mostra il messaggio e in caso di attesa scrive un avviso nel DIV di destinazione. La parte di codice pi articolata sar ovviamente quella di gestione dei dati in caso di successo, ovvero la funzione formatResult:
function formatResult(strXML){ var objDOM = jsXML.parseXMLDocument(strXML); var oResult = document.getElementById("result"); oResult.innerHTML= ""; //cancellazione contenuto precedente var oTable = oResult.appendChild( document.createElement("table") ); var oTr = oTable.insertRow(-1); //costruisce le intestazioni di riga for(var i=0;i<fields.length;i++){ var field = fields[i]; if(field.header){
122
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
var oTh = oTr.appendChild( document.createElement("th") ); oTh.innerHTML= field.caption; } } //XPath per estrarre i nodi <persona> var persone = objDOM.selectNodes("//persona"); for(var i=0;i<persone.length;i++){ var persona = persone[i] var oTr = oTable.insertRow(-1); for(var j=0;j<fields.length;j++){ var field = fields[j]; if(field.header){ var oTd = oTr.insertCell(-1); oTd.innerHTML= persona.getAttribute(field.attname); } } } }
Le operazioni in sequenza sono: G trasformare linput XML in forma di stringa in un documento DOM XML G ottenere una referenza del DIV allinterno del quale andr posizionata la tabella e cancellare leventuale contenuto G creare un nuovo oggetto table e utilizzare gli oggetti dellArray fields che hanno la propriet header su true per costruire la riga dellintestazione G estrarre dal documento DOM XML i nodi <persona> e scorrerli in un ciclo for creando un riga della tabella ad ogni passaggio. G Allinterno del primo ciclo for innestare un secondo ciclo che torna a scorrere gli oggetti dellArray fields , ne preleva la propriet attname (nome dellattributo) e la usa per estrarre il valore del corrispondente attributo del nodo <persona> e inserirlo in una nuova cella delI libri di ioPROGRAMMO/Programmare con Ajax
123
PROGRAMMARE
CON
Capitolo 5
la riga corrente.
Il risultato finale Il codice completo della procedura AJAX sar:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Esempio AJAX</title> <script language="javascript" type="text/javascript" src="jsXML.js"></script> <script language="javascript" type="text/javascript"> var fields = [ {"value":"CompanyName","caption":"Nome azienda", "attname":"company","header":true}, {"value":"ContactName","caption":"Nome contatto", "attname":"contact","header":true}, {"value":"Address","caption":"Indirizzo","attname":"address", "header":true}, {"value":"City","caption":"Citt","header":false}, {"value":"Country","caption":"Stato","header":false} ] function fillSelectFields(){ var oSel = document.getElementById("field"); for(var i=0;i<fields.length;i++){ var field = fields[i]; var oOpt = oSel.appendChild ( document.createElement("option") ); oOpt.value = field.value; oOpt.innerHTML= field.caption; } }
124
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
function sendRequest(){ var serverUrl = "server.php"; var searchValue = document.getElementById("search").value; var fieldValue = document.getElementById("field").value; var requestBody = "search=" + encodeURIComponent(searchValue) + "&field=" + encodeURIComponent(fieldValue); jsXML.sendAsyncPost(serverUrl, requestBody, formatResult, error, wait); } function formatResult(strXML){ var objDOM = jsXML.parseXMLDocument(strXML); var oResult = document.getElementById("result"); oResult.innerHTML= ""; //cancellazione contenuto precedente var oTable = oResult.appendChild( document.createElement("table") ); var oTr = oTable.insertRow(-1); //costruisce le intestazioni di riga for(var i=0;i<fields.length;i++){ var field = fields[i]; if(field.header){ var oTh = oTr.appendChild( document.createElement("th") ); oTh.innerHTML= field.caption; } } //XPath per estrarre i nodi <persona> var persone = objDOM.selectNodes("//persona"); for(var i=0;i<persone.length;i++){ var persona = persone[i] var oTr = oTable.insertRow(-1); for(var j=0;j<fields.length;j++){
I libri di ioPROGRAMMO/Programmare con Ajax
125
PROGRAMMARE
CON
Capitolo 5
var field = fields[j]; if(field.header){ var oTd = oTr.insertCell(-1); oTd.innerHTML= persona.getAttribute(field.attname); } } } } function error (msg){ alert(msg); } function wait(){ document.getElementById("result").innerHTML= "Attendi..." } window.onload = function () { fillSelectFields(); } </script> <link rel="STYLESHEET" type="text/css" href="style.css"> </head> <body> <div class="title">Selezione tabella clienti</div> <form onsubmit="sendRequest();return false"> <table class="layoutTable" cellpadding="2" cellspacing="0"> <tr> <td> <div class="label">Parola iniziale</div> <input class="inputBox" type="text" id="search" value=""> </td> <td> <div class="label">Nel campo</div> <select id="field"></select>
126
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
</td> <td valign="bottom"> <input type="submit" class="submit" id="" value="Invia"> </td> </tr> </table> </form> <div id="result"></div> </body> </html>
Il risultato nel browser sar quello visibile in figura 7, notate come ad ogni query la tabella venga popolata in background senza ricaricare la pagina.
127
PROGRAMMARE
CON
Capitolo 5
iniziale. Intendiamoci, non sempre questo un male, in questo caso ad esempio non un vero e proprio problema quanto piuttosto un comportamento logico dellapplicazione. Alcune volte per pu esserlo, pensiamo a una tabella con paginazione: lutente fa click su un prodotto che era a pagina 10 per vedere una scheda dettagliata, chiude la scheda e torna a pagina 1, questo decisamente un comportamento non desiderabile. In generale il problema della persistenza delle informazioni durante la navigazione una delle questioni su cui ferve il dibattito tra i Pro e i Contro AJAX. In realt ci sono validi sistemi per risolverlo, tra questi: G luso di frames nascoste G luso dei cookies
frames nascoste Forse il sistema migliore per garantire la persistenza delle informazioni durante la navigazione dellapplicazione costituito dalluso di frames nascoste, si tratta di incapsulare la pagina che ospita il codice AJAX in un frameset del tipo:
<html> <head> <title>Applicazione AJAX</title> </head> <frameset rows="0,*" framespacing="0" frameborder="0"> <frame src="hidden.html" name="hidden" <frame src="applicazioneAJAX.html" name="main"/> </frameset> </html> noresize/>
come potete notare dallattributo rows di <frameset> la prima frame sar di altezza 0 e quindi invisibile.
128
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
Nel file della pagina invisibile, hidden.html, potremo inserire delle variabili a livello globale in uno script come :
<script language="javascript" type="text/javascript"> var search = "sss"; </script>
dal codice della pagina visibile possibile leggere e scrivere questa variabile con:
var value = top.hidden.search; //lettura top.hidden.search = value; //scrittura
In questo modo la navigazione avverr tutta allinterno del frame main e qualsiasi pagina che vi si trova sar in grado di accedere alle variabili del frame hidden. Il vantaggio di questa tecnica che non legata ai cookies (che potrebbero essere disabilitati dallutente). Lo svantaggio, per contro, che non mette al riparo dal Refresh completo della pagina effettuato intenzionalmente o meno.
I cookies Un altro sistema per garantire la persistenza quello di usare i cookies ovvero le informazioni residenti in maniera pi o meno temporanea nel computer dellutente. I cookies sono accessibili a javascript attraverso la propriet document.cookie per gestire lettura scrittura dei cookies si pu usare del codice come:
var Cookies = new Object(); Cookies.get = function (name,defaultValue) { var nameEQ = name + "="; var ca = document.cookie.split(';');
I libri di ioPROGRAMMO/Programmare con Ajax
129
PROGRAMMARE
CON
Capitolo 5
for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) { var ckValue=c.substring(nameEQ.length,c.length); if(ckValue!='') return ckValue; } } return defaultValue; } Cookies.set = function (name,value) { var expires = ""; var days = arguments[2]; var path = (arguments[3])?arguments[3]:"/"; if (days) { if(!isNaN(new Number(days))) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); expires = "; expires="+date.toGMTString(); } } var setValue = name+"="+value+expires+"; path=" + path; document.cookie = setValue; } Cookies.remove = function (name){ Cookies.set(name,"",-1); }
in questo modo si hanno a disposizione le funzioni: G Cookies.get per leggere un valore dai cookies G Cookies.set per impostare un cookie
130
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
Nella nostra applicazione AJAX dovremo prevedere una funzione che si occupa del salvataggio dello stato ad ogni richiesta ed una che si occupa di ripristinare lo stato precedente a ogni reload, come ad esempio:
function recordInput(){ var inputs = ["search","field"]; for(var i=0;i<inputs.length;i++){ Cookies.set(inputs[i],document.getElementById(inputs[i]).value); } } function reloadInput() { var inputs = ["search","field"]; for(var i=0;i<inputs.length;i++){ document.getElementById(inputs[i]).value = Cookies.get(inputs[i],''); } if(Cookies.get("search",'')!='') sendRequest(); }
Luso dei cookies per la persistenza ha come vantaggi : G non richiede layout particolari come le frames G pu essere impostata anche una persistenza che va oltre la sessione G resiste al refresh della pagina Laspetto negativo invece solo uno, ma molto importante : se lutente disabilita i cookies addio persistenza. In generale comunque la persistenza non rappresenta poi quel problema che si vuole fare credere (anche perch spesso di usa AJAX come complemento a tecniche tradizionali); tuttavia in casi estremi, dove si vuole essere sicuri al 100%, nulla vieta di ricorrere a tutte e due le tecniche frame e cookies, in questo caso si avr una doppia registrazione e un doppio controllo (cookies e frame)
131
PROGRAMMARE
CON
Capitolo 5
5.4 NOSCRIPT
Un altro problema potrebbe essere il caso in cui lutente abbia disabilitato gli script del browser, la soluzione in questi casi avvertire l'utente del problema inserendo nella pagina un TAG <NOSCRIPT>, come ad esempio:
<NOSCRIPT> <table width="100%" cellpadding="0" cellspacing="0"> <tr> <td align="center"> <table cellpadding="4" cellspacing="0"> <tr> <td> <img src="gifs/warning.gif" border="0" align= "absmiddle" alt=""> </td> <td style="font:bold 14px arial;color:red"> Attenzione! il tuo browser ha disabilitato gli script.<br/> L'applicazione funziona solo con gli script abilitati. </td> </tr> </table> </td> </tr> </table> </NOSCRIPT>
Il contenuto del TAG resta invisibile nel caso di scripts abilitati mentre avverte l'utente in caso contrario.
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
software(come IBM, Sun e Microsoft), che talvolta oltre sviluppare linguaggi di programmazione producono anche ambienti di sviluppo, si stanno muovendo intorno al fenomeno AJAX cercando di integrare nei vari IDE soluzioni per gestire AJAX. Emblematica in questo senso la strada che ha scelto Microsoft con il nuovo Framework ASP.NET AJAX ( http://ajax.asp.net ). In pratica questa soluzione aggiunge altri WebControls a quelli disponibili: G che caricano nella pagina ASP.NET le librerie javascript necessarie G forniscono uninterfaccia per la gestione di timer, pannello di caricamento ecc Oltre ai nuovi controlli abbiamo poi delle librerie lato client che traducono la risposta del server in oggetti javascript in maniera trasparente eliminando la necessit di usare XML. Il lato server viene gestito con un Web Service come questo (lesempio ripreso dal sito sopra citato):
<%@ using System; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using Microsoft.Web.Script.Services; namespace Samples.AspNet { [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ScriptService] public class HelloWorldService : System.Web.Services.WebService { [WebMethod]
I libri di ioPROGRAMMO/Programmare con Ajax
WebService
Language="C#"
Class="Samples.AspNet.HelloWorldService" %>
133
PROGRAMMARE
CON
Capitolo 5
public string HelloWorld(String query) { string inputString = Server.HtmlEncode(query); if(!String.IsNullOrEmpty(inputString)) { return String.Format("Hello, you queried for {0}. The " + "current time is {1}", inputString, DateTime.Now); } else { return "The query string was null or empty"; } } } }
Lato client invece il codice di gestione viene inserito in una pagina ASP.NET :
<%@ Page Language="C#" Title="ASP.NET AJAX Script Walkthrough" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title="ASP.NET AJAX Script Walkthrough" /> <style type="text/css"> body { font: 11pt Trebuchet MS; font-color: #000000; padding-top: 72px; text-align: center } .text { font: 8pt Trebuchet MS } </style>
134
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
</head> <body> <form id="Form1" runat="server"> <asp:ScriptManager runat="server" ID="scriptManager"> <Services> <asp:ServiceReference path="./HelloWorldService.asmx" /> </Services> </asp:ScriptManager> <div> Search for <input id="SearchKey" type="text" /> <input id="SearchButton" type="button" value="Search" onclick="DoSearch()" /> </div> </form> <hr style="width: 300px" /> <div> <span id="Results"></span> </div> <script type="text/javascript"> function DoSearch() { var SrchElem = document.getElementById("SearchKey"); Samples.AspNet.HelloWorldService.HelloWorld(SrchElem.value, OnRequestComplete); } function OnRequestComplete(result) { var RsltElem = document.getElementById("Results"); RsltElem.innerHTML = result; } </script> </body>
I libri di ioPROGRAMMO/Programmare con Ajax
135
PROGRAMMARE
CON
Capitolo 5
</html>
Come possiamo vedere nella funzione javascript il metodo del Web Service stato mappato, in maniera trasparente, su un oggetto javascript. Esempi di questo approccio naturalmente possono trovarsi in tutti i linguaggi. Beh che dire? Non c dubbio che queste soluzioni funzionino a mio modesto parere tuttavia impararsi degli standard come XMLHttpRequest, DOM e XPath richiede pi o meno lo stesso tempo che imparare queste API, con il vantaggio che, una volta che abbiamo capito il meccanismo, siamo liberi di implementare le soluzioni AJAX con il linguaggio lato server che preferiamo e addirittura possiamo costruirci delle API tutte nostre da riutilizzare nei progetti.
136
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
o anche come :
var myArray = ["Mela","Pera","Banana"];
Le notazioni contratte si possono anche combinare, per cui un oggetto che ha una propriet di tipo Array, che con notazione estesa si sarebbe espresso:
var myObject = new Object(); myObject.width = 100; myObject.height = 20; myObject.frutti = new Array(); myObject.frutti[0]= "Mela"; myObject.frutti[1]= "Pera"; myObject.frutti[2]= "Banana"; con notazione contratta pu essere anche espresso come: var myObject = {"width":100,"height":20,"frutti":["Mela","Pera","Banana"]}
Quindi possiamo avere infinite strutture e sottostrutture capaci di rappresentare una grande variet di dati. A questo si aggiunga che javascript dotato del metodo globale eval che consente di interpretare una qualsiasi stringa come codice javascript, pertanto scrivere:
var myObject = {"width":100,"height":20,"frutti":["Mela","Pera","Banana"]}
o scrivere:
eval('var myObject
I libri di ioPROGRAMMO/Programmare con Ajax
137
PROGRAMMARE
CON
Capitolo 5
={"width":100,"height":20,"frutti":["Mela","Pera","Banana"]}');
la stessa cosa, tanto che potremmo poi invocare propriet e metodi con la solita sintassi, come:
alert (myObject.width);
Se torniamo un attimo a quello che abbiamo visto AJAX vediamo come una volta che recuperati i dati dal server in formato XML questi dovevano poi essere letti con il DOM e XPath per essere trasformati in HTML. Bene, lidea di JSON : perch il server invece di inviare i dati in XML non li manda direttamente in forma di stringa che Javascript possa interpretare come oggetto, come Array ecc? Ed infatti qui sta la differenza tra le due tecniche : luso di XML , in AJAX, e luso della notazione di javascript, in JSON.
AJAX vs JSON JSON in confronto ad AJAX ha vantaggi e svantaggi. I vantaggi di JSON sono: G con JSON il flusso dei dati pi leggero perch la struttura a TAG di XML pi prolissa (anche se si pu ridurre utilizzando, quando possibile gli attributi anzich gli elementi) G con JSON si elimina la fase di parsing dei dati,basta solo XMLHttpRequest per il trasporto AJAX invece ha dalla sua: G i dati sono in un formato standard come XML, oggi li utilizziamo per un applicazione Web, domani li potremmo utilizzare come Web Service o da unapplicazione Desktop senza toccare il lato server G il flusso XML in AJAX potrebbe anche essere bidirezionale, cio non solo il client che riceve XML dal server, ma anche il server pu ricevere XML dal client (e i linguaggi lato server sono certo pi attrezzati per interpretare lXML che una stringa di javascript) G il test e debug sul server pi facile analizzando flussi XML rispetto
138
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 5
Finalmente AJAX
AJAX
ad una stringa di javascript. il flusso XML oltre che analizzato e gestito con il DOM potrebbe essere anche direttamente trasformato con XSLT
In conclusione potremmo dire che sia AJAX che JSON sono due ottime tecniche, lideale sarebbe apprenderle entrambe per poi usare quella che si adatta di pi alla situazione specifica.
139
PROGRAMMARE CON
Capitolo 6
AJAX
141
PROGRAMMARE
CON
Capitolo 6
Server, Northwind. I nostri dati quindi sono su un file XML come questo:
<root> <Products ProductID="1" ProductName="Chai" QuantityPerUnit="10 boxes x 20 bags" UnitPrice="18" UnitsInStock="5" /> <Products ProductID="2" ProductName="Chang" QuantityPerUnit="24 - 12 oz bottles" UnitPrice="19" UnitsInStock="4" /> <Products ProductID="3" ProductName="Aniseed Syrup" QuantityPerUnit="12 - 550 ml bottles" UnitPrice="10" UnitsInStock="12"/> <Products ProductID="4" ProductName="Chef Anton's Cajun Seasoning" QuantityPerUnit="48 - 6 oz jars" UnitPrice="22" UnitsInStock="3"/> </root>
Nel file abbiamo una serie di elementi <Products> che rappresentano i prodotti, ognuno dei quali ha, come attributi: G ProductID ID del prodotto G ProductName Nome del prodotto G QuantityPerUnit Quantit per unit G UnitPrice Prezzo per unit G UnitsInStock Disponibilit di magazzino Naturalmente, in un caso reale i dati e la risposta del server saranno generati dalla procedura (ASP.NET, PHP, JSP ecc) di e-commerce, a noi interessa focalizzarci sul meccanismo lato client. Quindi andremo a scrivere un file HTML con lo pseudo-codice di un ipotetico carrello con i prodotti corrispondenti ai dati contenuti nel file XML che abbiamo sul server. La porzione di codice HTML in questione sar pressappoco cos:
<div class="title">Ordine</div> <form> <table cellpadding="1" cellspacing="2">
142
PROGRAMMARE CON
Capitolo 6
AJAX
<tr> <th>Codice</th> <th>Nome articolo</th> <th>Unit per confezione</th> <th>Prezzo per Unit</th> <th colspan="2">Quantit</th> </tr> <tr> <td>1</td> <td>Chai</td> <td>10 boxes x 20 bags</td> <td align="right">18</td> <td align="right"><input type="text" class="qta" id="prodotto1" value="3"></td> <td id="alert1"></td> </tr> <tr> <td>2</td> <td>Chang</td> <td>24 - 12 oz bottles</td> <td align="right">19</td> <td align="right"><input type="text" class="qta" id="prodotto2" value="2"></td> <td id="alert2"></td> </tr> <tr> <td>3</td> <td>Aniseed Syrup</td> <td>12 - 550 ml bottles</td> <td align="right">10</td> <td align="right"><input type="text" class="qta" id="prodotto3" value="1"></td> <td id="alert3"></td>
I libri di ioPROGRAMMO/Programmare con Ajax
143
PROGRAMMARE
CON
Capitolo 6
</tr> <tr> <td>4</td> <td>Chef Anton's Cajun Seasoning</td> <td>48 - 6 oz jars</td> <td align="right">22</td> <td align="right"><input type="text" class="qta" id="prodotto4" value="5"></td> <td id="alert4"></td> </tr> <tr> <td colspan="6" align="right"> <input type="submit" class="submit" value="Invia"> </td> </tr> </table> </form>
E il risultato nel browser sar quello visibile in figura 8. figura 8 La form nel carrello (Immagini\8.psd) Nella struttura HTML possiamo notare come : G in ogni riga presente una casella di testo per definire la quantit con ID corrispondente alla parola prodotto seguita dallID del prodotto corrispondente. G a lato della casella di testo c una cella vuota con ID corrispondente alla parola alert seguita dallID del prodotto corrispondente. Inseriamo quindi, nel codice della pagina HTML, il riferimento alla libreria di funzioni AJAX e XML di cui abbiamo parlato nel capitolo precedente, jsXML:
<script src="jsXML.js" type="text/javascript"></script>
144
PROGRAMMARE CON
Capitolo 6
AJAX
ed impostiamo una funzione che controlla la disponibilit del prodotto esaminando il file XML di dati presente sul server (che avremo chiamato carrello.xml):
function controllaDisponibilita(inputBox){ //controlla se il valore un numero var value = new Number(inputBox.value); if (isNaN(value)) { alert("Il valore non un numero valido"); inputBox.value=0; return } var currentID = inputBox.id.replace("prodotto",""); //Sub funzione di callback su ricevimento dati var dataArrive = function(strXML){ //creazione del XMLDocument var doc = jsXML.parseXMLDocument(strXML); //individuazione del nodo prodotto var nodoProdotto = doc.selectSingleNode("//Products[@ProductID='" + currentID + "']"); //recupero del valore var unitsInStock = nodoProdotto.getAttribute("UnitsInStock"); //cella messaggio var alertCell = document.getElementById("alert" + currentID); //confronto valori if (unitsInStock < value) { //se il valore maggiore al max viene visualizzato il messaggio alertCell.innerHTML= '<span style="color:red">' + 'La quantit impostata superiore al valore massimo di ' + unitsInStock + '</span>'; } else {
I libri di ioPROGRAMMO/Programmare con Ajax
145
PROGRAMMARE
CON
Capitolo 6
//se il valore corretto viene cancellato un eventuale //messaggio precedente alertCell.innerHTML= ''; } } jsXML.sendAsyncGet("carrello.xml","",dataArrive) }
Questa funzione richiede come parametro lelemento <input> HTML che contiene il valore della quantit ed effettua le seguenti operazioni: G In primo luogo esamina se il valore della casella un numero e, in caso contrario, avvisa lutente. G Poi ricava lID del prodotto corrente dallid dellelemento <input> che, come abbiamo detto, rispetta la convenzione di denominazione prodotto{n} dove {n} lid del prodotto. G Quindi imposta una sotto-funzione, chiamata dataArrive, che verr richiamata al ricevimento del file XML da parte del server e recupera il nodo del prodotto, legge il valore della quantit di merce in magazzino, lo confronta con quello impostato dallutente e, eventualmente, inserisce un messaggio di errore nella cella adiacente. G Da ultimo c la chiamata vera e propria della funzione contenuta nella libreria jsXML sendAsyncGet che si occupa di scaricare in modo asincrono i dati XML dal server e passarli alla sotto-funzione dataArrive in precedenza esaminata. Non resta a questo punto che agganciare la funzione controllaDisponibilita ad un evento, scegliamo di collegarla allevento onblur (quando lutente esce dalla casella) di ogni casella che contiene le quantit, in questo modo:
<input onblur="controllaDisponibilita(this)" type="text" id="prodotto1" value="3">
Notiamo che come parametro viene passato this ovvero il controllo cor146
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 6
AJAX
rente. In questo modo quando lutente sceglie un valore superiore alla quantit massima verr visualizzato il messaggio di cui in figura 9. figura 9 Il messaggio di errore in tempo reale (Immagini\9.psd) Naturalmente questo solo un esempio che non prende in considerazione il lato server, il file XML sul server infatti nel nostro caso statico, ad esso va sostituita una pagina dinamica che colloquia con il database, vediamo come potrebbe essere in ASP.NET.
147
PROGRAMMARE
CON
Capitolo 6
End Property Public Function QueryXml() As String Dim conn As New SqlConnection("Data Source=MIOSERVER;Initial Catalog=Northwind;Persist Security Info=True") Dim strSQL As String = "SELECT * FROM Products Where ProductID=@ID FOR XML AUTO" Dim cmd As New SqlCommand(strSQL, conn) Dim param As SqlParameter = cmd.CreateParameter() param.ParameterName = "@ID" param.Value = IDProdotto cmd.Parameters.Add(param) Dim strResult As String = "" conn.Open() Dim reader As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection) While reader.Read strResult &= reader.GetString(0) End While reader.Close() Return strResult End Function Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest context.Response.ContentType = "text/xml" With context.Response.Output .WriteLine("<root>") .Write(QueryXml) .WriteLine("</root>") End With End Sub
148
PROGRAMMARE CON
Capitolo 6
AJAX
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class
Naturalmente anche in questo caso si tratta di un esempio puramente esplicativo: nessuno mai si sognerebbe di mettere la stringa di connessione direttamente nel codice (c per questo il file Web.config) e sarebbe anche bene separare le query SQL o nella configurazione o in file distinti; facciamo cos solo per brevit. A questo punto lunica modifica al codice AJAX sar quella di cambiare la url di risposta del server, se prima era :
jsXML.sendAsyncGet("carrello.xml","",dataArrive)
149
PROGRAMMARE
CON
Capitolo 6
Dove currentID, lo ricordiamo, era il codice del prodotto incluso nellid dellelemento <input>
150
PROGRAMMARE CON
Capitolo 7
AJAX
151
PROGRAMMARE
CON
Capitolo 7
G G
so luso di alcuni browser o labilitazione di alcune funzioni per le Intranet/Extranet, per le stesse ragioni di cui sopra per funzioni di complemento, utilit che magari velocizzano il lavoro dellutente ma non sono essenziali al funzionamento dellapplicazione
7.4 I TOOLS
Di tools e librerie per AJAX ne esistono un po per tutti i linguaggi di programmazione e gli ambienti di sviluppo, solo per citarne alcuni: G Google Web Toolkit http://code.google.com/webtoolkit che traduce in AJAX applicazioni scritte in Java G Eclipse AJAX Toolkit Framework http://www.eclipse.org/atf - che integra tool di sviluppo AJAX nel popolare IDE G AJAX ASP.NET http://ajax.asp.net gi citato in precedenza, che rappresenta la proposta di Microsoft per lintegrazione di AJAX con ASP.NET e Visual Studio Comunque si pu tranquillamente affermare che, una volta apprese le basi concettuali della tecnica, lunico tool che realmente uti152
I libri di ioPROGRAMMO/Programmare con Ajax
PROGRAMMARE CON
Capitolo 7
AJAX
le un buon editor HTML (come UltraEdit o Homesite) per il lato client e lambiente di sviluppo comunemente usato per il lato server.
7.5 RIFERIMENTI
Alcuni link utili sono : http://www.w3schools.com per AJAX e le altre tecnologie Web http://www.topxml.com con interessanti e completi tutorial sullargomento http://sourceforge.net/projects/sarissa - la popolare libreria per la gestione di XML con javascript http://developer.mozilla.org/en/docs/AJAX:Getting_Started un tutorial firmato Mozilla http://msdn.microsoft.com/library - la famosa library di Microsoft che nella sezione Web offre numerosi e utili riferimenti per javascript e HTML http://www.adaptivepath.com/publications/essays/archives/000385.php - larticolo di Jesse James Garrett, colui che ha coniato il termine AJAX http://www.xml.com altro sito con interessanti articoli su XML e AJAX http://www.w3.org il sito del W3C che contiene tutti i riferimenti agli standard di cui abbiamo parlato
153
NOTE
i libri di
EDITORE Edizioni Master S.p.A. Sede di Milano:Via Ariberto, 24 - 20123 Milano Sede di Rende: C.da Lecco, zona ind. - 87036 Rende (CS)
Realizzazione grafica: Cromatika Srl C.da Lecco, zona ind. - 87036 Rende (CS) Art Director: Paolo Cristiano Responsabile grafico di progetto: Salvatore Vuono Coordinatore tecnico: Giancarlo Sicilia Illustrazioni: Tonino Intieri Impaginazione elettronica: Francesco Cospite
Servizio Clienti
Stampa: Grafica Editoriale Printing - Bologna Finito di stampare nel mese di Gennaio 2007
Il contenuto di questopera, anche se curato con scrupolosa attenzione, non pu comportare specifiche responsabilit per involontari errori, inesattezze o uso scorretto. Leditore non si assume alcuna responsabilit per danni diretti o indiretti causati dallutilizzo delle informazioni contenute nella presente opera. Nomi e marchi protetti sono citati senza indicare i relativi brevetti. Nessuna parte del testo pu essere in alcun modo riprodotta senza autorizzazione scritta della Edizioni Master.