In questo articolo realizziamo un carrello della spesa con tecnologia ASP.NET, quindi appoggiandoci al NET Framework e al web server di casa Microsoft, IIS. Fin qui nulla di nuovo, potremmo creare delle pagine ASP.NET ed un database, che potrebbe essere Access, e il gioco sarebbe fatto. Ma vogliamo andare oltre e sfruttare la tecnologia della chiamata asincrona, quindi dovremo utilizzare in parallelo anche un Framework che ci fornisca supporto per AJAX e supporto per il Client.
Configurazione
In questo esempio utilizziamo Ajax.NET, che abbiamo già illustrato in un precedente articolo. Scarichiamo il componente. Nel file compresso c'è una dll che si chiama Ajax.dll ed un piccolo file per le istruzioni.
Il passo successivo sarà quello di creare un nuovo progetto web all'interno di Visual Studio o Visual Studio Express che chiameremo "AjaxKart" che sarà poi anche il nome del namespace del progetto. Una volta creato il progetto dovremo inserire la cartella "Bin" nella quale andremo a posizionare la dll per del supporto Ajax.
Il secondo passo per rendere produttivo il nostro ambiente di lavoro è quello di configurare l'HttpHandler per Ajax, indicando quale sarà la factory che si dovrà preoccupare di creare il proxy tra la tecnologia Server e quella Client. Per fare cio'dobbiamo andare a configurare il file web.config del progetto.
Listato 1. Configurazione web.config
<httpHandlers>
<add verb="POST,GET" path="Ajax/*.ashx" type="Ajax.PageHandlerFactory, Ajax" />
</httpHandlers>
Una spiegazione dettagliata di quello che abbiamo fatto la potete trovare nell'articolo che spiega l'utilizzo di questa tecnologia. In breve, abbiamo assegnato le chiamate Ajax, ad un handler specifico ovvero la "Ajax.dll".
Il sistema
Un carrello della spesa, deve principalmente offrire la possibilità di:
- Riconoscere l'utente tramite una pagina di Login
- Consentire la navigazione tra i prodotti
- Mostrare un carrello personale con l'elenco dei prodotti selezionati
- Effettuare il checkOut finale
Andiamo di passo in passo. La gestione dell'autenticazione la risolviamo utilizzando un'autenticazione di tipo Form, ovvero un'autenticazione personalizzata che andrà a prelevare le informazioni dal Database. Per la navigazione tra i prodotti costruiamo una pagina che, sfruttando la tecnologia Ajax e i WebServices messi a disposizione da ASP.NET, fornisca un motore di ricerca per i prodotti. Infine un'ultima pagina verrà utilizzata per gestire gli ordini effettuati dall'utente stesso.
Lasciamo al lettore l'implementazione di una pagina per la creazione e gestione degli utenti e una pagina per l'inserimento ed eliminazione di prodotti dal Database. Nell'esempio viene fornito il progetto NET completo di sorgenti e base dati con records.
Il Database
Per questo progetto utilizziamo un Database di tipo Access 2003. Un esempio lo trovate nei files allegati al progetto. Apriamo Access 2003, creiamo un nuovo database (Base_dati.mdb) e lo salviamo nella cartella "App_Data" del nostro progetto web. Ricordiamo che la cartella App_Data è una cartella speciale creata da Visual Studio, studiata appositamente per contenere file come database, XML e simili. Saranno già pronti i permessi su questa cartella per consentire la lettura del database.
Il nostro database sarà composto da 4 tabelle:
- UTENTI: dovrà contenere le informazioni utente utili per l'autenticazione nel nostro carello. Sono presenti 3 campi:
- ID: impostato come "Contatore",
- UTENTE: di tipo testuale,
- PASSOWRD sempre di tipo testuale
Le altre informazioni relative l'utente le andremo ad inserire in una seconda tabella, questa soluzione viene adottata per non appesantire il traffico in rete durante le fasi di autenticazione.
- UTENTI_ANAGRAFICA contiene le altre informazioni sugli utenti e sarà in relazione 1 a 1 con la tabella "UTENTI". In questo modo abbiamo un record fisico presente nella parte di anagrafica per ogni record presente nella tabella utenti. Questa tabella è composta da campi di tipo Stringa che memorizzano le varie informazioni anagrafiche dell'utente, oltre che ad un campo "ID_UTENTE" di tipo numerico, che servirà per identificare il record della tabella UTENTI correlato.
- PRODOTTI contiene le informazioni sui prodotti e al suo interno avremo i campi:
- ID per identificare il singolo record
- CODICE per identificare e ricercare i prodotti
- NOME di tipo testuale
- DESCRIZIONE sempre di tipo testo
- IMMAGINE dove memorizzeremo l'immagine del prodotto, o meglio il path dell'immagine
- PREZZO di tipo Double per memorizzarne il prezzo
- ORDINI sarà la tabella che gestirà lo storico degli ordini. In pratica per ogni ordine effettuato dal cliente verrà indicata la DATA dell'ordine nonchè l'ID dell'utente che ha effettuato l'ordine e l'ID del prodotto ordinato.
La pagina di Login
Per poter accedere al nostro carrello, vogliamo essere sicuri che l'utente sia registrato nel database. Dovremo quindi implementare nel nostro progetto una logica personalizzata di autenticazione. Utilizziamo per questo l'autenticazione di tipo "Forms" fornita da ASP.NET.
Il primo passo è quello di proteggere le nostre pagine da sguardi indiscreti. Creiamo quindi una sottocartella che chiameremo "Carrello" ed indichiamo, tramite il web.config, che qualsiasi utente provi ad accedere al nostro carrello, dovrà essere prima autenticato. Completiamo anzitutto il file web.config che riguarda la cartella principale del progetto.
Listato 2. web.config generale
<authentication mode="Forms" >
<forms loginUrl="Login.aspx" defaultUrl="Default.aspx" path="/">
</forms>
</authentication>
In questo file abbiamo definito il tipo di autenticazione (Forms), abbiamo impostato la pagina che gestirà l'autenticazione (loginUrl) e la pagina verso la quale verranno indirizzati gli utenti autenticati (defaultUrl). Ora andiamo nella cartella Carrello e inseriamo un nuovo web.config per proteggerla.
Listato 3. web.config per la cartella carrello
<authorization>
<deny users="?"/>
</authorization>
Adesso sappiamo che chiunque proverà ad accedere alla nostra zona riservata ai Clienti dovrà prima essere autenticato dalla pagina di Login.
Veniamo alla creazione della pagina di Login. Per questa pagina abbiamo bisogno di 3 informazioni, ovvero nome utente, password e la richiesta di mentenere le informazioni di login tramite i cookie. Con quest'ultima opzione non verrà richiesta l'autenticazione ad ogni accesso al carrello, ma solamente la prima volta.
Listato 4. Pagina di Login
<form id="MyForm" runat="server">
<tr>
<td><label for="txtUtente">Username : </label></td>
<td> </td>
<td><asp:TextBox ID="txtUtente" runat="server" Width="100px"></asp:TextBox></td> </tr>
<tr>
<td><label for="txtPassword">Password : </label></td>
<td> </td>
<td><asp:TextBox ID="txtPassword" runat="server" TextMode="Password" Width="100px"></asp:TextBox></td>
</tr>
<tr>
<td><label for="chkRicorda">Ricorda : </label></td>
<td> </td>
<td><asp:CheckBox ID="chkRicorda" runat="server" /></td>
</tr>
<td><asp:Button ID="btnLogin" runat="server" Text="Login" /></td>
A questo punto siamo in grado di creare la prima funzione "server" per gestire il login. In questo caso non ci avvaliamo delle potenzialità fornite da AJAX.NET perchè vogliamo reinviare l'utente alla pagina principale del carrello o al carrello stesso in base alla scelta da lui effettuata. Per prima cosa dobbiamo creare nel database una Stored Procedure che riceverà due parametri, @USERNAME
e @PASSWORD
e ci restituirà un record nel caso l'utente esista oppure nulla se l'utente non presente nel Database.
Listato 5. Stored Procedure CERCA_UTENTE
SELECT UTENTI.[UTENTE], UTENTI.[PASSWORD]
FROM UTENTI
WHERE UTENTI.[UTENTE] = @USERNAME
AND UTENTI.[PASSWORD] = @PASSWORD;
Adesso dobbiamo creare nella pagina di Login due funzioni. La prima andrà a verificare se l'utente che sta provando ad autenticarsi esiste, sfruttando la Stored Procedure appena creata, mentre la seconda funzione sarà quella scatenata direttamente dal Click del pulsante. Nel listato 6 troviamo la funzione generale che viene scatenata alla pressione del tasto di login.
Listato 6. Click pulsante di login.
Protected Sub btnLogin_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnLogin.Click
Dim ricorda As Boolean = chkRicorda.Checked
If UtenteValido(txtUtente.Text, txtPassword.Text) Then
FormsAuthentication.RedirectFromLoginPage(txtUtente.Text, ricorda)
Else
lblError.Text = "Utente non valido o password errata."
lblError.ForeColor = Drawing.Color.DarkRed
End If
End Sub
Se l'utente è presente nel database, la funzione UtenteValido
restituisce un valore "true", quindi possiamo validare il login tramite la classe FormsAuthentication, in caso contrario l'utente non è valido e rispondiamo con un messaggio di errore in una label presente nella pagina di login.
Creiamo la funzione che andrà a leggere i dati nel database e ci indicherà se l'utente è presente o no.
Listato 7. Funzione di validazione
Public Function UtenteValido(ByVal Username As String, ByVal Password As String) As Boolean
Dim cn As OleDbConnection
Try
' Connessione Al Database
Dim strConn As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & MapPath("App_Data/Base_Dati.mdb") & ";Persist Security Info=False"
cn = New OleDbConnection(strConn)
' Command per eseguire la Stored Procedure
Dim CMD As New OleDbCommand()
Dim strQuery As String = "CERCA_UTENTE"
CMD.Connection = CN
CMD.CommandText = strQuery
CMD.CommandType = Data.CommandType.StoredProcedure
' Parametri
Dim userParam As New OleDbParameter("@USERNAME", Username)
Dim passParam As New OleDbParameter("@PASSWORD", Password)
CMD.Parameters.Add(userParam)
CMD.Parameters.Add(passParam)
' Apertura e lettura dati
cn.Open()
Dim DR As OleDbDataReader = CMD.ExecuteReader(Data.CommandBehavior.CloseConnection)
If DR.HasRows Then
' L'utente esiste
Return True
Else
' L'utente non esiste
Return False
End If
DR.Close()
Catch ex As Exception
Trace.Write(ex.Message)
Return False
Finally
' Chiusura connessione se aperta
If cn.State <> Data.ConnectionState.Closed Then
cn.Close()
End If
End Try
End Function
La funzione di validazione verifica la possibilità di connettersi al dabatase: se fallisce restituisce "false" e l'utente non può essere validato, in caso contrario, viene creato un OleDbCommand
con i parametri per la stored procedure. Il comando viene eseguito e restituisce i record ad un oggetto DataAdapter
. Usiamo la proprietà HasRows
per verificare la presenza dell'utente. Alla fine chiudiamo DataReader
e connessione.
Volendo perfezionare il tutto, possiamo gestire anche un messaggio di errore se l'utente viene reindirizzato alla pagina di login perché non autenticato. Magari utilizzando la sintassi User.Identity.IsAuthenticated
nell'evento Load
della pagina di login.
Il carrello
Finalmente possiamo concentrarci sul nostro carrello e sul contenuto di "Carrello.aspx" che abbiamo precedentemente creato nella cartella protetta "Carrello".
Il carrello sarà composto principalmente da 2 pannelli. Il primo pannello conterrà il motore di ricerca dei prodotti ed i prodotti trovati di volta in volta. L' altro pannello invece ci fornirà le informazioni sull'utente e su i suoi acquisti. Per abbellire il sistema che stiamo creando, in questo articolo abbiamo utilizzato degli elementi grafici ed un foglio di stile che potete trovare nell'esempio allegato. Andiamo per ordine analizzando prima il pannello utente ed il checkOut del carrello e poi il motore per il carrello stesso.
Il Pannello Utente
Per la parte relativa le informazioni dell'utente dobbiamo creare un contenitore <div>
per le informazioni e una serie di label che tramite AJAX andremo a popolare con le informazioni prelevate dal database. Per prima cosa andiamo a costruire una Stored Procedure che questa volta ci restituirà tutte le informazioni relative il nostro utente, ricevendo come parametro di input la username dell'utente stesso.
Listato 8. Stored Procedure VISUALIZZA_UTENTE
SELECT
UTENTI.UTENTE,
UTENTI_ANAGRAFICA.NOME, UTENTI_ANAGRAFICA.COGNOME,
UTENTI_ANAGRAFICA.INDIRIZZO, UTENTI_ANAGRAFICA.CITTA,
UTENTI_ANAGRAFICA.PAESE, UTENTI_ANAGRAFICA.EMAIL
FROM
UTENTI INNER JOIN UTENTI_ANAGRAFICA ON UTENTI.ID = UTENTI_ANAGRAFICA.UTENTE_ID
WHERE
UTENTI.UTENTE = @USERNAME;
La nostra Stored Procedure effettua una selezione dei dati completi dell'utente, tramite un JOIN
tra l'ID dell'utente. Come parametro utilizziamo il valore dell'account utente che abbiamo precedentemente memorizzato durante la fase di autenticazione e che possiamo prelevare dal sistema utilizzando la sintassi User.Identity.Name.
Passiamo alla creazione del pannello informazioni utente. Per memorizzare queste informazioni utilizziamo un <div>
come contenitore, al quale poi assoceremo uno stile CSS per abbellimento. In questo contenitore posizioniamo un <fieldset>
per rendere più professionale il pannello; all'interno del fieldset inseriamo una tabella per impaginare le varie label che conterranno le informazioni.
Listato 9. Contenitore Informazioni Utente
<div id="info_utente">
<fieldset>
<legend>Informazioni Utente</legend>
<table border="0">
<tr> <td><strong>Nome :</strong></td>
<td> </td>
<td><span id="lblUtente"></span></td>
<td><strong>Cognome :</strong></td>
<td> </td>
<td><span id="lblCognome"></span></td> </tr>
</table>
</fieldset>
</div>
Procediamo in questo modo per tutte le informazioni che vogliamo visualizzare, quindi Indirizzo, Paese e così via.
Adesso dobbiamo creare la funzione ASP.NET che andrà a leggere i dati dal database e retituirà una DataTable che andremo poi ad interpretare tramite Javascript. Per far si che questa funzione sia visibile dal Proxy di AJAX dobbiamo inserire un attributo [Ajax.AjaxMethod]
prima della funzione. Inoltre dobbiamo registrare la pagina ASP.NET nel Proxy.
Listato 10. Caricamento Pagina
'Registrazione del Proxy
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Ajax.Utility.RegisterTypeForAjax(GetType(Carrello_Carrello))
End Sub
Nell'evento Load della pagina abbiamo registrato la nostra classe cosi'che da adesso, tutti i metodi "pubblici" marcati come [AjaxMethod]
saranno visibili in Javascript. La funzione che segue viene eseguita per prelevare i dati dal database. È una funzione presente nella pagina ASP.NET ma verrà eseguita in modalità asincrona da AJAX.
Listato 11. Reperimento Informazioni
'Reperimento Informazioni
<Ajax.AjaxMethod()>
Public Function Visualizza_Utente() As DataSet
Dim cn As OleDbConnection
Dim ds As New DataSet
Dim username As String = User.Identity.Name
Try
'Connessione Al Database
Dim strConn As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:--Base_Dati.mdb;Persist Security Info=False"
cn = New OleDbConnection(strConn)
'Command per eseguire la Stored Procedure
Dim CMD As New OleDbCommand()
Dim strQuery As String = "VISUALIZZA_UTENTE"
CMD.Connection = cn
CMD.CommandText = strQuery
CMD.CommandType = Data.CommandType.StoredProcedure
'Parametri
Dim userParam As New OleDbParameter("@USERNAME", username)
CMD.Parameters.Add(userParam)
'DataAdapter per leggere i dati
Dim DA As New OleDbDataAdapter(CMD)
DA.Fill(ds)
Return ds
Catch ex As Exception
Trace.Write(ex.Message)
Return ds
Finally
'Chiusura connessione se aperta
If cn.State <> Data.ConnectionState.Closed Then
cn.Close()
End If
End Try
End Function
Questa funzione non fa altro che connettersi al database, utlizzando la solita connessione OleDb, prelevare i dati tramite la Stored Procedure che abbiamo creato prima e inserirli all'interno di un dataset. In questo modo potremo lavorare direttamente sulla proprietà del dataset, tipo la DataTable
, le Rows
e così via un po' per tutte le caratteristiche che un dataset è in grado di esporre. Questa procedura la utlizzeremo per parecchie chiamate e la logica generale per leggere i dati è sempre quella appena esposta.
Le funzioni JavaScript
Ma veniamo alla parte più interessante del pannello utente. Le funzioni Javascript. Adesso dovremo creare due funzioni Javascript. Una sarà semplicemente la chiamata alla funzione presente nella pagina ASP.NET, quindi una chiamata alla funzione lato server; mentre la seconda funzione Javascript sarà il callback della prima, utile per gestire gli errori in caso di eccezioni non gestite.
Listato 12. Chiamata Javascript al metodo server
//chiamata principale
function visualizza_utente()
{
Carrello_Carrello.Visualizza_Utente(visualizza_callback);
}
Chiaramente Carrello_Carrello
è il nome della classe proprietaria del metodo, che abbiamo registrato prima con Ajax.Utility.RegisterTypeForAjax(GetType(Carrello_Carrello))
mentre visualizza_callback
non è altro che una funzione che dobbiamo creare noi per getire meglio il valore ricevuto.
Listato 13. Funzione Javascript di callback
//callBack per gestire errori
function visualizza_callback(risultato)
{
if (risultato.error != null)
{
alert("Errore nel caricamento dati!");
}
else
{
//prelevo DataSet
var ds = risultato.value;
document.getElementById("lblNome").innerHTML = ds.Tables[0].Rows[0].NOME;
document.getElementById("lblCognome").innerHTML = ds.Tables[0].Rows[0].COGNOME;
document.getElementById("lblIndirizzo").innerHTML = ds.Tables[0].Rows[0].INDIRIZZO;
document.getElementById("lblCitta").innerHTML = ds.Tables[0].Rows[0].CITTA;
document.getElementById("lblPaese").innerHTML = ds.Tables[0].Rows[0].PAESE;
}
}
Ecco il nostro parser bello che pronto senza aver speso troppe righe di codice. Analizziamolo nel dettaglio. Se non ci sono valori o se viene rilasciata un'eccezione a livello di ASP.NET, la funzione restituisce un alert con il messaggio di errore, al contrario, popola le label con i valori presenti nel dataset. Il dataset creato e serializzato da AJAX.NET è fortemente tipizzato a tal punto che al posto di inserire la dicitura COLUMN("Nome_Colonna")
ci basta inserire il nome vero e proprio del campo del database, come se fosse una proprietà dell'oggetto dataset che stiamo leggendo.
A questo punto la prima parte del carrello è pronta. Al caricamento della pagina, in modalità completamente asincrona, andiamo a caricare i dati dell'utente che sta navigando il carrello, senza bloccare la pagina e sopratutto senza interferire con le prestazioni del carrello stesso.
Il prossimo passo sarà quello di creare il pannello che si occuperà di visualizzare e filtrare i prodotti presenti nel database ed esporli in un tabella.
In questa parte dell'articolo vogliamo inserire un contenitore per effettuare il controllo dell'ordine (checkOut) e realizzare il motore di ricerca per i prodotti.
Il CheckOut
Questo pannello dovrà visualizzare di volta in volta gli ordini effettuati dall'utente, filtrandoli per data. Applichiamo questo criterio perché vogliamo usare la data come "timestamp" per visualizzare solamente gli ordini dell'utente nella sessione di lavoro corrente. Questa soluzione ci serve solo per esempio, in generale sarabbe più opportuno utilizzare degli ID di sessione.
Per la creazione dobbiamo posizionare un nuovo <div>
sotto quello che contiene le informazioni utente. In questo <div>
saranno presenti due pulsanti per:
- Aggiornare la visualizzazione degli ordini
- Calcolare il totale dell'ordine
Infine avremo un <div>
che conterrà le informazioni che verranno inserite dinamicamente da JavaScript. Nel database Access creiamo una sola Stored Procedure che estrae tutti gli ordini di un determinato utente, effettuati oggi. In questo modo possiamo sia calcolare il totale dell'ordine contando i vari ordini, sia visualizzarli all'utente.
Listato 14. Stored Procedure per estrarre gli ordini di un utente
SELECT
UTENTI.ID, ORDINI.DATA,
PRODOTTI.CODICE, PRODOTTI.NOME, PRODOTTI.PREZZO AS PREZZO, ORDINI.QUANTITA, ORDINI.PREZZO
FROM
UTENTI INNER JOIN (PRODOTTI INNER JOIN ORDINI ON PRODOTTI.ID = ORDINI.ID_PRODOTTO)
ON UTENTI.ID = ORDINI.ID_UTENTE
WHERE
UTENTI.ID=[@UTENTE] AND ORDINI.DATA>=[@DATA];
Con questa SELECT effettuiamo un collegamento tra 3 tabelle, tramite l'ID dell'utente e ricaviamo le righe dell'ordine che sta effettuando. Queste informazioni le filtriamo tramite il nome utente @UTENTE
e tramite la data di acquisto @DATA
.
Come per il pannello informazioni utente, anche qui dobbiamo creare una routine dal lato server che andrà ad eseguire la Stored Procedure e ci fornirà come valore di ritorno un dataset contenente i risultati. Questa routine la andremo poi ad associare ad un callback in JavaScript, come prima.
Listato 15. Funzione lato Server per visualizzare gli ordini
'Visualizza un ordine
<Ajax.AjaxMethod()> Public Function Controlla_Carrello(ByVal utente As String) As DataSet
Dim cn As OleDbConnection
Dim ds As New DataSet
Try
'Connessione Al Database
Dim strConn As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=..AjaxKartApp_DataBase_Dati.mdb;Persist Security Info=False"
cn = New OleDbConnection(strConn)
'Command per eseguire la Stored Procedure
Dim CMD As New OleDbCommand()
Dim strQuery As String = "CARRELLO"
CMD.Connection = cn
CMD.CommandText = strQuery
CMD.CommandType = Data.CommandType.StoredProcedure
'Parametri
Dim utenteParam As New OleDbParameter("@UTENTE", utente)
CMD.Parameters.Add(utenteParam)
Dim dataParam As New OleDbParameter("@DATA", Date.Now.ToShortDateString)
CMD.Parameters.Add(dataParam)
'DataAdapter per leggere i dati
Dim DA As New OleDbDataAdapter(CMD)
DA.Fill(ds)
Return ds
Catch ex As Exception
Throw New Exception("Errore nella ricerca." & ex.Message)
Return ds
Finally
'Chiusura connessione se aperta
If cn.State <> Data.ConnectionState.Closed Then
cn.Close()
End If
End Try
End Function
Listato 16. Lato client callback JavaScript
//aggiorna carrello
function carrello()
{
var utente = document.getElementById("id_utente");
Carrello_Carrello.Controlla_Carrello(utente.innerHTML, carrello_callback);
}
//callback per il carello
function carrello_callback(risultato)
{
if (risultato.error != null)
{
alert(risultato.error + "nErrore nel caricamento dati!");
}
else
{
//prelevo DataSet
var ds = risultato.value;
var carrello = document.getElementById("carrello");
var checkout = document.getElementById("checkout");
prodotti.style.visibility = "visible";
if (ds != null && typeof(ds) == "object" & ds.Tables[0].Rows[0] != null)
{
var prod = "";
prod += "<table border=0 class='tabellaCheckout'width='100%'>";
prod += "<tr><td><strong>Codice</strong></td>";
... ... ...
checkout.innerHTML = prod;
L'ultima funzione permette di aggiornare il pannello "checkout" ed inserire una tabella con tutti gli ordini effettuati dall'utente. Per fare ciò associamo questa funzione alla pressione di un pulsante o di un immagine come nell'esempio che segue.
Listato 17. Pannello Checkout
<div id="carrello">
<fieldset>
<legend>Carrello</legend>
<p>Elenco dei prodotti selezionati.</p>
<p> Aggiorna il tuo ordine </p>
</fieldset>
<div id="checkout">
</div>
<span id="lblTotale"> </span>
<img src="../Immagini/Aggiorna.gif" alt="aggiorna ordine" onclick="carrello();" style="cursor:hand" />
<img src="../Immagini/CheckOut.gif" alt="checkout" onclick="checkout();" style="cursor:hand" />
Come si può vedere dal listato 17, abbiamo un <div>
vuoto che riempiremo con gli ordini. Al di sotto un'etichetta che conterrà il totale dell'ordine. Infine due immagini, una per visualizzare l'ordine aggiornato tramite la funzione appena creata, ed uno per calcolare il totale. Questa funzione possiamo inserirla anche nell'evento onLoad
della pagina, in questo modo se l'utente dovesse perdere la sessione potrebbe risalire ai suoi ordini semplicemente riaprendo la pagina.
Adesso dobbiamo calcolare il totale degli ordini. Per riciclare un po' di codice, utilizziamo la stessa funzione server di prima ma al posto di retituire il dataset trovato, dobbiamo restituire un valore double
contenente il totale. Per calcolarlo, dobbiamo estrarre tutti gli ordini effettuati oggi dall'utente, e tramite un loop, andare a sommare i vari perzzi degli ordini in un unica variabile di tipo double
. Nel listato 18 troviamo un sunto di questa funzione, il restante codice è uguale a quello della funzione del listato 17.
Listato 18. Funzione server per calcolare il totale ordine
'Esegue il controllo dell'ordine
<Ajax.AjaxMethod()> Public Function checkout(ByVal utente As String) As Double
'Calcoliamo il totale
Dim dt As DataTable = ds.Tables(0)
Dim totale As Double = 0.0
For Each row As DataRow In dt.Rows
totale += row("PREZZO")
Next
Return totale
Listato 19. CallBack JavaScript
//esegue il totale
function checkout()
{
var utente = document.getElementById("id_utente");
Carrello_Carrello.checkout(utente.innerHTML, checkout_callback);
}
//checkout utente
function checkout_callback(risultato)
{
if (risultato.error != null)
{
alert(risultato.error + "nErrore.");
}
else
{
//prelevo DataSet
var totale = risultato.value;
var prod = "";
prod += "<table border=0>";
prod += "<tr>";
prod += "<td><strong>Totale ordine :</strong>" + totale + "
€</td>";
prod += "</tr>"; prod += "</table>";
var lblTotale = document.getElementById("lblTotale");
lblTotale.innerHTML = prod;
}
}
Adesso siamo pronti per effettuare degli ordini nel nostro carello. L'utente ha una sezione dove puo'verificare la correttezza dei suoi dati anagrafici e dove verrà inviato l'ordine, ed una zona sottostante dove puo'controllare il suo ordine.
Questo pannello verrà posto alla destra dello schermo. Per adesso non ci preoccupiamo del floating, il pannello prodotti che andremo a creare adesso avrà un float:left
e si posizionerà alla sinistra dello schermo facendo si che il carrello venga posto alla destra come vogliamo noi.
Il motore di ricerca
Per il motore di ricerca abbiamo bisogno di un singolo contenitore <div>
che andremo a posizionare con un valore di float:left
. In questo pannello dobbiamo insrire un motore di ricerca, che tramite l'inserimento di "codice" e "nome" prodotto, per estrapolare i dati, ed un pannello per visualizzarli e farli selezionare all'utente.
Inseriamo nel <div>
"prodotti" delle label e delle texbox per effettuare la ricerca ed un pulsante.
Listato 20. Pannello di ricerca
<div id="contenuto">
<fieldset>
<legend>Prodotti</legend>
<p>In questa sezione è possibile ricercare e selezionare i prodotti presenti a catalogo.</p>
<table border="0">
<tr> <td><label for="txtCodice"><strong>Codice :</strong></label></td> <td> </td> <td><input type="text" id="txtCodice" class="textBox" /></td> </tr>
<tr> <td><label for="txtNome"><strong>Nome :</strong></label></td> <td> </td> <td><input type="text" id="txtNome" class="textBox" /></td> <td> </td>
<td><img src="../Immagini/ordini.gif" alt="Ricerca" onclick="trova_prodotto();" style="cursor: hand" /></td> </tr>
</table>
</fieldset>
</div>
<div id="prodotti">
<fieldset>
<legend>Prodotti</legend>
<p>Elenco dei prodotti trovati.</p>
<div id="elenco"></div>
</fieldset>
</div>
Nella parte superiore inseriremo, di volta in volta, le informazioni per ricercare i prodotti, e nel pannello sotto compariranno delle righe in base ai records trovati. A questo punto ci serve una nuova query. Questa volta sarà una SELECT solamente della tabella "prodotti", ma che riceverà come parametri il codice e il nome prodotto. Dobbiamo usare un LIKE
, perchè cosi'se l'utente, nel campo codice, inserisce una 'A', noi visualiziamo tutti i prodotti con codice che inizia per 'A'.
Listato 21. Stored Procedure TROVA_PRODOTTO
SELECT
PRODOTTI.ID, PRODOTTI.CODICE, PRODOTTI.NOME, PRODOTTI.DESCRIZIONE, PRODOTTI.PREZZO
FROM
PRODOTTI
WHERE
PRODOTTI.CODICE Like [@CODICE] And PRODOTTI.NOME Like [@NOME];
Come per i pannelli precedenti, dobbiamo creare una funzione lato server per effettuare la ricerca e il suo callback in JavaScript.
Listato 22. Funzione ASP.NET per ricercare i prodotti.
'Trova Prodotti
<Ajax.AjaxMethod()> Public Function Trova_Prodotto(ByVal codice As String, ByVal nome As String) As DataSet
...
Dim strQuery As String = "TROVA_PRODOTTO"
...
Dim codiceParam As New OleDbParameter("@CODICE", codice & "%")
CMD.Parameters.Add(codiceParam)
Dim nomeParam As New OleDbParameter("@NOME", nome & "%")
CMD.Parameters.Add(nomeParam)
Abbiamo sostituito solamente il nome della Stored Procedure e i parametri che riceve. Il restante codice non cambia. Inoltre abbiamo "concatenato" il valore di un parametro con un simbolo '%'. Questa funzione dice ad Access di prelevare "tutti i record il cui campo x inizia per un certo valore". Così se l'utente lascia i campi vuoti, il motore estrarrà tutti i record presenti. Invece se verrà digitato 'AB' nel campo codice, il motore estrarrà tutti i record con il codice che inizia per 'AB'.
Richiamiamo, tramite l'ausilio del solito callback JavaScript, anche questa funzione e restituiamo una tabella contenente i recorsd trovati, oppure un semplice messaggio.
Listato 23. Callback JavaScript per ricerca prodotti
//ricerca prodotti
function trova_prodotto()
{
var codice = document.getElementById("txtCodice");
var nome = document.getElementById("txtNome");
Carrello_Carrello.Trova_Prodotto(codice.value, nome.value, trova_callback);
}
function trova_callback(risultato)
{
if (risultato.error != null)
{
alert(risultato.error + "nErrore nel caricamento dati!");
}
else
{
... ...
Nella prima parte della funzione andiamo, come prima, a verificare se ci sono righe nel dataset, se non ce ne sono l'estrazione non ha generato risultati. Se abbiamo risultati, dobbiamo inserire una tabella con i record, specificando il nome, codice e descrizione prodotto. Di fianco, ogni prodotto avrà una casella di testo contenente la quantità che l'utente vuole selezionare e un pulsante per ordinare quel prodotto con quella quantità.
Listato 24. Creazione record prodotti
if (ds != null && typeof(ds) == "object" & ds.Tables[0].Rows[0] != null) {
var prod = "";
prod += "<table border=0 class='tabellaCarrello'>";
for(var x = 0; x < ds.Tables[0].Rows.length; x++) {
prod += "";
prod += "<tr class='rigaTabellaCarrello'>";
prod += "<td>";
prod += ds.Tables[0].Rows[x].ID;
prod += "</td>";
prod += "<td> </td>";
prod += "<td>";
prod += ds.Tables[0].Rows[x].CODICE;
prod += "</td>";
prod += "<td> </td>";
prod += "<td>";
prod += ds.Tables[0].Rows[x].NOME;
...
...
prod += "<input type='text'id='txt_" + ds.Tables[0].Rows[x].CODICE + "'class='textBoxCarrello'value=0 />"
prod += "</td>"; prod += "<td> </td>"; prod += "<td>";
prod += "<img src='../Immagini/ordina.gif'alt='ordina'style='cursor:hand;'
onClick='inserisci_prodotto(" + ds.Tables[0].Rows[x].ID + ", " + ds.Tables[0].Rows[x].PREZZO +
", " + "txt_" + ds.Tables[0].Rows[x].CODICE + ");'/>";
...
elenco.innerHTML = prod;
Per la casella di testo dobbiamo fornire un nome univoco che sarà composto da una parte costante 'txt_' seguita dal codice del prodotto. Mentre nel pulsante andremo a richiamare una funzione JavaScript, che sarà il callback di una routine ASP.NET che inserisce il prodotto selezionato, assegnandolo all'ordine dell'utente che sta navigando il carrello.
La parte del motore di ricerca è completata. Non ci resta che incapsulare tutta la logica del database in una Stored procedure che sia in grado di assegnare dei prodotti ad un determinato utente, tramite la tabella ordini.
Inserimento degli ordini e aggiornamento.
La tabella "ordini" contiene in ogni record, l'ID dell'utente, l'ID del prodotto che ha selezionato, la quantità di prodotto, la data di acquisto e un campo prezzo che sarà creato al momento dalla Stored, calcolando il prezzo del prodotto per la quantità scelta. In questo modo il totale di ogni ordine lo calcoliamo noi nella Stored Procedure. Volendo possiamo anche inserire un IF nella sintassi SQL per aggiungere uno sconto dopo una certa quantità di prodotto comperato.
Il listato 25 mostra la Stored Procedure INSERISCI_ORDINE.
Listato 25. Stored Procedure INSERISCI_ORDINE
INSERT INTO ORDINI
( ID_UTENTE, ID_PRODOTTO, QUANTITA, DATA, PREZZO )
SELECT [@ID_UTENTE] AS Expr1,
[@ID_PRODOTTO] AS Expr2,
[@QUANTITA] AS Expr3,
[@DATA] AS Expr4,
[@QUANTITA]*[@PREZZO] AS Expr5;
La Stored Procedure va ad inserire nel database, il valore estrapolato da una SELECT che riceve i parametri sopra elencati. Adesso come per tutto il nostro carrello, dobbiamo creare la routine Server Side che esegue la Stored ed il solito callback JavaScript.
Listato 26. Procedura Server
'Inserisce un ordine
<Ajax.AjaxMethod()> Public Sub Inserisci_Ordine _
(ByVal id_utente As Int32, ByVal id_prodotto As Int32, ByVal quantita As Int32, ByVal prezzo As Double)
Dim strQuery As String = "INSERISCI_ORDINE"
'Parametri
Dim utenteParam As New OleDbParameter("@ID_UTENTE", id_utente)
CMD.Parameters.Add(utenteParam)
Dim prodottoParam As New OleDbParameter("@ID_PRODOTTO", id_prodotto)
CMD.Parameters.Add(prodottoParam)
Dim quantitaParam As New OleDbParameter("@QUANTITA", quantita)
CMD.Parameters.Add(quantitaParam)
Dim dataParam As New OleDbParameter("@DATA", Date.Now.ToShortDateString)
CMD.Parameters.Add(dataParam)
Dim prezzoParam As New OleDbParameter("@PREZZO", prezzo)
CMD.Parameters.Add(prezzoParam)
Dim x As Int32 = CMD.ExecuteNonQuery()
If x <= 0 Then
Throw New Exception("Errore nell'inserimento dati.")
End If
Questa volta abbiamo modificato anche il modo di eseguire la Stored Procedure. A noi non interessa ricevere come risultato un record ma solamente, volgiamo sapere se il record dell'ordine è stato inserito. Dobbiamo usare quindi il metodo ExecuteNonQuery
dell'OleDbCommand
. Questo metodo restituisce un valore Integer che indica il numero di righe modificate, aggiunto o rimosse dal database. Controllando il suo valore sappiamo se è stato inserito o meno l'ordine.
Adesso non ci resta che creare il callback per inserire l'ordine. Anche qui la funzione sarà piu'semplice perchè dobbiamo solamente richiamare il metodo server, passargli i parametri e eventualmente segnalare l'errore.
Listato 27. Callback per inserire un ordine
//inserimento prodotto
function inserisci_prodotto(id_prodotto, prezzo, casella)
{
if (casella.value == 0)
{
alert("Inserire una quantità superiore a 0.");
return;
}
var id_utente = document.getElementById("id_utente");
Carrello_Carrello.Inserisci_Ordine(id_utente.innerHTML, id_prodotto, casella.value, prezzo, inserisci_callback);
casella.value = 0;
}
function inserisci_callback(risultato)
{
if (risultato.error != null)
{
alert(risultato.error + "nErrore nel inserimento dati!");
}
else
{
//aggiorna
carrello carrello();
}
}
Un'ultima nota è quella di richiamare nella funzione di callback per inserire gli ordini, un aggiornamento alla funzione che visualizza i prodotti selezionati. Questo automatismo fa si che ad ogni aggiunta di un prodotto nell'ordine che l'utente sta effettuando si aggiorna in automatico il carrello.
Conclusioni
Con questo articolo abbiamo simulato solo un esempio di realizzazione delle classiche interazioni di un utente con un sito e-commerce, utilizzando però il framework AJAX.NET per rendere l'esperienza d'uso più fluida. È un esempio e come tale ha i suoi limiti, ma crediamo di aver dato uno spunto per nuove elaborazioni sul tema.