JavaScript

5. Leggere i form HTML

Leggere i form HTML

Vediamo di seguito alcuni metodi che ci consentiranno di leggere rapidamente tutti i dati contenuti in una form, e di aggiungerli all'array params, pronti per essere processati e inviati nella prossima richiesta AJAX.
Il metodo principale (e l'unico che richiameremo) è form_read.
Quest'ultimo si servirà di altri due metodi esterni solo nel caso in cui, nella form HTML che dovrà leggere, ci sarà una select multipla.

Il primo metodo d'appoggio è getSelected, che legge i valori selezionati nella select multipla e restituisce un'array contenente i suddetti valori.
Il secondo metodo d'appoggio è storeSelected, che prenderà l'array elaborato da getSelected e lo memorizzerà in params pronto per la spedizione.

AjaxModule.prototype.getSelected = function(opt)
{
	var selected = new Array();
	var index = 0;

	for (var intLoop = 0; intLoop < opt.length; intLoop++)
	{
		if ((opt[intLoop].selected) || (opt[intLoop].checked))
		{
			index = selected.length;
			selected[index] = new Object;
			selected[index].value = opt[intLoop].value;
			selected[index].index = intLoop;
		}
	}

	return selected;
}

AjaxModule.prototype.storeSelected = function(opt, p)
{
	var sel = AjaxModule.prototype.getSelected(opt);
	var strSel = "";

	var cont = 0;
	for (var item in sel)
	{
		cont++;
		AjaxModule.prototype.params[p + "_" + cont] = sel[item].value;
	}
	AjaxModule.prototype.params["n_" + p] = cont;
}

AjaxModule.prototype.form_read = function(nome_form)
{
	var oForm = document.getElementById(nome_form);

	for (var i = 0; i < oForm.elements.length; i++)
	{
		var oField = oForm.elements[i];

		switch (oField.type)
		{
			case "checkbox" :
			case "radio" :
				if (oField.checked)
					AjaxModule.prototype.params[oField.id] = "1";
				else AjaxModule.prototype.params[oField.id] = "0";
				break;

			case "text" :
			case "hidden" :
			case "password" :
			case "textarea" :
			case "select-one" :
				AjaxModule.prototype.params[oField.id] = oField.value;
				break;

			case "select-multiple" :
				AjaxModule.prototype.storeSelected(oField.options, oField.id);
				break;
		}
	}
}

Per utilizzare questa nuova funzionalità è sufficiente assegnare un ID alla form che dobbiamo leggere ed utilizzare il seguente codice per estrapolarne i dati.

<script type="text/javascript" language="JavaScript">
	imp.form_read('nome_form');
</script>

Una volta chiamato form_read, tutti i valori contenuti nel form HTML saranno pronti e disponibili in params e non dovrete fare altro che effettuare la richiesta al server attraverso il metodo makeRequest.

Il codice di esempio, e il codice della classe AjaxModule, li trovate in allegato a questo post.

E con questo abbiamo concluso il nostro tutorial.
Buon AJAX a tutti!

4. Esempio pratico

Esempio pratico

Arrivati a questo punto del tutorial, la libreria AjaxModule è completa e pronta per essere utilizzata.
Vediamo quindi un esempio pratico che vi sia utile per ricreare rapidamente un ambiente semplice, usabile e funzionale.

Nel nostro esempio interrogheremo uno script PHP che ci restituirà la lista degli ultimi impiegati, aspettandosi come parametro num_impiegati che utilizzerà per limitare la lista.
Passandogli quindi 0 (ZERO) come valore per num_impiegati, lo script ci restituirà una lista vuota, mentre se gli passeremo 3 (TRE) lo script restituirà la lista degli ultimi tre impiegati inseriti (se ci sono).

Non vedremo qui il codice PHP, ma sarà scaricabile a fine tutorial così potrete analizzarne il codice di esempio. Vediamo il codice JavaScript che eredita da AjaxModule per sfruttarne le funzionalità.

Impiegati.js

function Impiegati()
{
	this.setTargets = function(target_div)
	{
		this.setTarget(document.getElementById(target_div));
		this.setErrorTarget(document.getElementById(target_div));
		this.setLoadingTarget(document.getElementById(target_div));
	}

	this.carica = function()
	{
		this.setTargets('target');
		this.params['num_impiegati'] = document.getElementById('num_impiegati').value;
		this.makeRequest('script/impiegati.php');
	}
}

Impiegati.prototype = new AjaxModule();
Impiegati.prototype.constructor = Impiegati;

Il metodo setTargets non fa altro che impostare lo stesso target per la barra di caricamento, la risposta del server e gli errori di connessione.
Il metodo carica richiama setTargets, poi legge il numero di impiegati da un campo input nella pagina memorizzandolo nell'array params.
A questo punto utilizza makeRequest specificando il percorso della pagina PHP da interrogare.

Il metodo makeRequest apre automaticamente la connessione e invia i parametri trovati in params.
Ora vediamo la pagina HTML che includerà ed utilizzerà lo script "Impiegati" che ci consentirà di creare un'istanza della suddetta classe.
Attraverso l'istanza potremo effettuare la chiamata vera e propria al server per leggere la famigerata lista degli ultimi impiegati.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>

		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
		<title>Ajax Module</title>

		<script type="text/javascript" language="JavaScript" src="script/AjaxModule.js"></script>

		<script type="text/javascript" language="JavaScript" src="script/Impiegati.js"></script>
		<script type="text/javascript" language="JavaScript">

			// <!--
			var imp = new Impiegati();
			// -->
		</script>
	</head>
	<body>
		<a href="javascript:;" onclick="imp.carica();">Carica lista impiegati</a>

		<br />
		Numero massimo di ultimi impiegati da visualizzare:
		<input type="text" id="num_impiegati" name="num_impiegati" value="2" />

		<br />
		<div id="target" name="target"></div>
	</body>
</html>

Abbiate cura di includere i fogli JavaScript nell'ordine giusto per non generare incompatibilità con qualche browser.

Il tutorial non è ancora finito. Nel prossimo caiptolo vedremo alcuni strumenti utili per la lettura veloce di form HTML.

3. Gestire le risposte

Impostare la destinazione

Per prima cosa diamo un'occhiata agli attributi che dovranno memorizzare le impostazioni su cui si baseranno i metodi della classe una volta ricevuta la risposta dal server.

AjaxModule.prototype.loadingSrc = "immagini/loading.gif";
AjaxModule.prototype.loadingTarget = document.body;
AjaxModule.prototype.target = document.body;
AjaxModule.prototype.errorTarget = document.body;

Gli attributi target, errorTarget e loadingTarget memorizzano rispettivamente gli oggetti di destinazione che ospiteranno o gestiranno la risposta del server, un'eventuale errore (es. richiesta fallita, pagina non trovata) e la barra di caricamento.

Più avanti vedremo che sarà possibile assegnare ad essi un oggetto del DOM (es. un DIV) oppure una funzione che manipolerà direttamente l'output.
Di default la destinazione di entrambi sarà il body della pagina.

L'attributo loadingSrc memorizza semplicemente il percorso dove risiede l'immagine che verrà visualizzata durante i caricamenti.
Ora vedremo i metodi che ci consentiranno di assegnare dei valori agli attributi appena esposti.

AjaxModule.prototype.setLoadingTarget = function(new_target)
{
	if (typeof(new_target) == "object")
	{
		try { AjaxModule.prototype.loadingTarget = new_target; }
		catch (e) { throw new Error(err); }
	}
	else throw new Error(new String("Target non valido, impossibile gestire correttamente la risposta!"));
}

La funzione setLoadingTarget consente di impostare come target per la barra di caricamento solamente un oggetto. Se quindi vogliamo che il contenitore per la barra sia un DIV con id loader, utilizzeremo il seguente codice:

<div id="loader"></div>

<script type="text/javascript" language="JavaScript">
	// <!--
	Esempio.setLoadingTarget(document.getElementById('loader'));
	// -->
</script>

Vi consiglio di strutturare la IF con il typeof secondo le precise esigenze della vostra applicazione.
Volendo potete mettere l'istruzione getElementById direttamente all'interno della funzione in modo da passargli solo l'id del contenitore in formato stringa.
Vediamo ora come il metodo setTarget sarà in grado di gestire sia oggetti che funzioni per manipolare l'output, aggiungendo semplicemente una condizione alla IF:

AjaxModule.prototype.setTarget = function(new_target)
{
	var err = new String("Target non valido, impossibile gestire correttamente la risposta!");
	if (typeof(new_target) == "object" || typeof(new_target) == "function")
	{
		try { AjaxModule.prototype.target = new_target; }
		catch (e) { throw new Error(err); }
	}
	else throw new Error(err);
}

In questo caso sarà possibile specificare come destinazione anche una funzione a cui sarà devoluto l'intero output per un'eventuale manipolazione, come può essere ad esempio il parsing di un file XML o la trasformazione di una stringa url-encoded in un'array.
Anche qui vi consiglio di personalizzare il più possibile il metodo per le vostre esigenze.

Vediamo si seguito il metodo setErrorTarget, praticamente uguale a setTarget. Ho lasciato volontariamente le due funzioni separate in modo da potrele personalizzarle al meglio per le esigenze dell'applicazione che servirà.
Ricordatevi che potete sempre ridefinire la funzione una volta che ereditate da AjaxModule, senza quindi obbligarvi a riscrivere AjaxModule, sfruttando così il vantaggio di avere del codice riusabile in ogni caso.

AjaxModule.prototype.setErrorTarget = function(new_target)
{
	var err = new String("Target non valido, impossibile gestire correttamente la risposta!");
	if (typeof(new_target) == "object" || typeof(new_target) == "function")
	{
		try { AjaxModule.prototype.errorTarget = new_target; }
		catch (e) { throw new Error(err); }
	}
	else throw new Error(err);
}

Gestione della risposta

Il metodo cruciale è handleResponse.
Qualunque sia la risposta del server, passerà di qua.
Principalmente il metodo controllerà gli stati della comunicazione ed agirà di conseguenza fino a che non si concluderà la comunicazione. A quel punto, se non ci sono stati intoppi, handleResponse devolverà la risposta del server a un oggetto o ad una funzione, secondo come voi lo avete impostato attraverso i metodi visti in precendenza: setTarget e setLoadingTarget.

AjaxModule.prototype.handleResponse = function()
{
	if (AjaxModule.prototype.xmlhttp.readyState >= 1 &&
		AjaxModule.prototype.xmlhttp.readyState <= 3)
	{
		if (AjaxModule.prototype.doLoading === true)
			AjaxModule.prototype.loadingGIF();
	}
	else if (AjaxModule.prototype.xmlhttp.readyState == 4)
	{
		if (AjaxModule.prototype.xmlhttp.status == 200)
		{
			var response = AjaxModule.prototype.xmlhttp.responseText;

			if (typeof(AjaxModule.prototype.target) == "object")
				AjaxModule.prototype.target.innerHTML = response;
			else if (typeof(AjaxModule.prototype.target) == "function")
				AjaxModule.prototype.target(response);
			else throw new Error("Target non valido, impossibile gestire correttamente la risposta!");
		}
		else
		{
			try
			{
				if (typeof(AjaxModule.prototype.errorTarget) == "object")
				{
					var contentHTML = "<b>Errore durante il trasferimento dati.</b>";
					contentHTML += "<br />" + AjaxModule.prototype.xmlhttp.statusText;
					contentHTML += " (" + AjaxModule.prototype.xmlhttp.status + ")";
					document.getElementById(AjaxModule.prototype.errorTarget).innerHTML = contentHTML;
				}
				else (typeof(AjaxModule.prototype.errorTarget) == "function")
				{
					AjaxModule.prototype.errorTarget
					(
						AjaxModule.prototype.xmlhttp.statusText,
						AjaxModule.prototype.xmlhttp.status
					);
				}
			} catch (e) {}
		}
	}
}

In tutti i browser, gli stati da 1 a 3 vengono richiamati pi&uagrave; volte e sono precedenti al completamento della comunicazione (stato 4), possiamo quindi usarli per visualizzare un'eventuale barra di caricamento.

2. Inviare e memorizzare i dati

Inviare dati

Nel capitolo precedente abbiamo visto come creare l'oggetto XMLHttpRequest che sarà il responsabile delle connessioni asincrone.

Prima di vedere i metodi adibiti all'invio dei dati, diamo un'occhiata al codice che ci consentirà di organizzare, memorizzare e preparare i dati prima dell'invio vero e proprio al server.
Per fare questo ci serviremo di un Array che memorizzerà tutti i dati e di due metodi (funzioni):

  • makeRequest : apre la connessione attraverso XMLHttpRequest e la configura
  • sendRequest : prende i parametri dall'array e li invia al server

Memorizzazione dei dati

L'attributo che memorizzerà i nostri dati, sarà un'array di nome params.
Vediamo di seguito il codice ed un metodo per ripulirlo, che verrà automaticamente richiamato dalla classe alla fine di ogni invio dati.

AjaxModule.prototype.params = [];

AjaxModule.prototype.clearParams = function()
{ AjaxModule.prototype.params = []; }

Per aggiungere un nuovo parametro da inviare nella prossima richiesta, è sufficiente aggiungerlo a params mediante la classica sintassi utilizzata per assegnare i valori negli array associativi, avendo cura di riferirsi all'array attraverso il puntatore this.

this.params['nome_variabile'] = 'valore_variabile';

Aprire la connessione AJAX e inviare i dati

Vedremo ora il metodo che dovrà aprire e configuare la connessione.
All'interno del metodo noterete l'introduzione di una variabile doLoading. Questa variabile verrà controllata quando verrà ricevuta la risposta dal server per decidere se deve essere visualizzata una barra di caricamento o meno.

La sua utilità la vedremo quindi più avanti, ora concentriamoci sul metodo makeRequest:

AjaxModule.prototype.doLoading = false;

AjaxModule.prototype.makeRequest = function(sPage, mL)
{
	if (mL === true)
		AjaxModule.prototype.doLoading = false;
	else AjaxModule.prototype.doLoading = true;

	AjaxModule.prototype.xmlhttp.open("POST", sPage, true);
	AjaxModule.prototype.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
	AjaxModule.prototype.xmlhttp.onreadystatechange = AjaxModule.prototype.handleResponse;
	AjaxModule.prototype.sendRequest();
}

Le ultime quattro sono le istruzioni che ci interessano.
La prima riga utilizza il metodo open dell'oggetto XMLHttpRequest. Questo metodo apre la connessione con la pagina desiderata, impostando il metodo di comunicazione che verrà utilizzato per inviare i dati, ossia GET o POST.

Il metodo open prende inoltre altri 3 parametri:

  1. async : specifica se utilizzeremo una richiesta asincrona o sincrona. Noi lo imposteremo ad asincrona specificando quindi il metodo che dovrà gestire la risposta assegnando una funzione anonima al gestore di eventi onreadystatechange
  2. username : serve per specificare un'eventuale username nel caso in cui sia necessaria un'autenticazione
  3. password : come sopra ma specifica la password

Nella seconda riga vediamo il metodo setRequestHeader che serve per impostare gli header che verranno utilizzati per la chiamata al server.
Nel nostro caso lo utilizzeremo solamente per impostare li tipo di contenuto ed il charset usato. E' possibile impostare altre opzioni per l'header semplicemente chiamando il metodo per ogni impostazione rispettando la coppia "nome header", "valore".

Nella terza riga specifichiamo il metodo che verrà utilizzato per gestire la risposta del server. Vi ricordo che abbiamo esplicitamente dichiarato che effettueremo una richiesta di tipo asincrona grazie ad open.

L'ultima riga utilizza invece il metodo sendRequest che fa parte della stessa classe AjaxModule. Vediamo di seguito il suo codice:

AjaxModule.prototype.sendRequest = function()
{
	var pars = new String();

	for (var i in AjaxModule.prototype.params)
	{
	    pars += encodeURIComponent(i) + "=";
	    pars += encodeURIComponent(AjaxModule.prototype.params[i]) + "&";
	}

	AjaxModule.prototype.clearParams();
	pars = pars.substr(0, (pars.length - 1));
	AjaxModule.prototype.xmlhttp.send(pars);
}

Il metodo sendRequest non fa altro che scorrere l'array params organizzandolo in formato url-encoded nella stringa pars.
Utilizziamo la funzione nativa di JavaScript "encodeURIComponent" per ricodificare il dato evitando che caratteri speciali come i due punti o il simbolo della percentuale compromettano l'integrità del dato.

Poi utilizziamo il metodo clearParams per evitare che vengano reinviati gli stessi dati in una richiesta successiva.
Togliamo un punto e virgola di troppo con substr e poi inviamo tutto al server attraverso il metodo "send" dell'oggetto XMLHttpRequest.

Nel prossimo capitolo vedremo i metodi per gestire al meglio la risposta del server.

1. L'oggetto XMLHttpRequest

Ereditarietà in JavaScript

Non è obbiettivo di questo tutorial trattare con approfondimento l'ereditarietà, che vedremo in comunque in poche righe in modo da rendervi capaci di seguire il tutorial senza problemi.

In JavaScript non esiste un vero e proprio supporto per l'ereditarietà, per poter sfruttare quindi questa opzione ci serviremo dell'oggetto prototype.
Ci basta sapere che tutti i metodi e gli attributi che assegneremo a prototype, saranno disponibili anche agli altri attributi e ai metodi della classe istanziati attraverso il costruttore.

Per creare quindi una classe Figlio che eredita da un'ipotetica classe Padre, è sufficiente utilizzare questa metodologia:

function Padre() {}
Padre.prototype.funzione_padre = function()
{
	alert("ciao");
}

function Figlio()
{
	this.funzione_padre();
}

Figlio.prototype = new Padre();
Figlio.prototype.constructor = Figlio;

Se ora creiamo un'istanza della classe Figlio, questa farà partire l'alert mandando in video la stringa "ciao".

Il costruttore di AjaxModule

Il costruttore della classe AjaxModule non contiene istruzioni.
Lo dichiariamo semplicemente per permettere a JavaScript di creare le istanze.

Di seguito il codice del costruttore. E' sufficiente creare una funzione qualunque e utilizzare poi l'operatore new per creare un'istanza della classe.

function AjaxModule() {}

L'oggetto XMLHttpRequest

Il primo vero metodo che andiamo a vedere è createXMLHttpRequest.
Attraverso questo metodo garantiamo la compatibilità con tutti i browser attualmente in uso dall'utenza media e non, comprese le vecchie versione di Internet Explorer, che esattamente come le nuove (Internet Explorer 7 e 8) non seguono affatto gli standard consigliati dal consorzio internazionale e ci obbligano a ricorrere agli ActiveX per creare il nostro oggetto XMLHttpRequest
(si nota il sarcasmo?)

Ora vediamo il codice del suddetto metodo:

AjaxModule.prototype.createXMLHttpRequest = function()
{
	if (typeof XMLHttpRequest != "undefined")
	{ return new XMLHttpRequest(); }
	else if (window.ActiveXObject)
	{
		var versioni_IE = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "Microsoft.XMLHttp"];
		
		for (var i in versioni_IE)
		{
			try
			{
				var xmlobj = new ActiveXObject(versioni_IE[i]);
				return xmlobj;
			} catch (e) {}
		}
	}

	throw new Error("Impossibile inizializzare l'oggetto XMLHttp.");
}

Nella prima IF controlliamo che il tipo di dato XMLHttpRequest esista, ed è il caso di Firefox, Opera, Chrome, Safari e tutti gli altri.

Nella ELSE corrispondente invece ho racchiuso il codice per Internet Explorer.
Con l'uscita delle versioni successive di Internet Explorer, anche l'ActiveX corrispondente ci obbliga ad utilizzare una dicitura diversa per la creazione del medesimo oggetto.

Nell'array versioni_IE ho riportato le versioni ActiveX in ordine cronologico a partire dalla più recente.
In questo modo nella FOR ci serviamo del costrutto TRY e CATCH per "tentare" la creazione dell'oggetto in questione. In caso di fallimento il CATCH non esegue alcuna istruzione e passa all'iterazione successiva della FOR per provare con un ActiveX più vecchio.

Ajax Script

Introduzione

In questo tutorial costruiremo assieme uno script AJAX che ci consentirà di interrogare in modo semplice e veloce degli script lato server (o semplici pagine HTML).
Si tratta di uno script AJAX pronto ed efficace, che attraverso il concetto di ereditarietà, vi darà la possibilità di creare al volo una classe per comunicare con il vostro server.

Nel nostro caso costruiremo una pagina HTML che chiamerà uno script in PHP.
Questo restituirà a sua volta una lista di impiegati leggendola direttamente da un database MySQL.

Il cuore del tutorial consiste principalmente nella costruzione del modulo in JavaScript che dovrà occuparsi dell'invio e della ricezione dei dati.

Consiglio il tutorial a chi è già avvezzo con la programmazione ad oggetti in JavaScript, anche se non lo ritengo un requisito strettamente necessario per una comprensione utile.
Se utilizzate comunque l' ereditarietà in altri linguaggi, allora potrebbe essere il momento opportuno per imparare qualche chicca in JavaScript

Iniziamo con la creazione del foglio JS che ospiterà la nostra classe madre: AjaxModule.

Condividi contenuti