Grazie alla sua struttura fortemente modulare ASP.Net MVC è una tecnologia che si integra molto facilmente con diverse librerie JavaScript, consentendo la creazione di applicazioni moderne e interattive. In questo tutorial vedremo come sfruttare le potenzialità di JQuery, libreria molto apprezzata per la sua leggerezza e facilità di utilizzo, per la realizzazione di un'interfaccia di registrazione utente basata su Ajax.
Le funzionalità che implementeremo nel tutorial sono principalmente tre:
- notifica istantanea della disponibilità dello username scelto dall'utente
- validazione lato client del form
- spedizione del form di registrazione tramite Ajax
Durante il tutorial utilizzeremo Visual Studio 2008 ed ASP.Net MVC 1.0, tuttavia tutte le nozioni dovrebbero essere applicabili anche nel caso in cui stiate lavorando con Visual Web Developer Express Edition, Visual Studio 2010 o ASP.Net MVC 2.0.
Allegato all'articolo è disponibile il progetto di esempio da scaricare, utile come riferimento aggiuntivo.
Predisporre l'ambiente
Avviamo Visual Studio e creiamo un nuovo progetto ASP.Net MVC avendo cura di selezionare C# come linguaggio di programmazione ed il framework .Net 3.5 come piattaforma.
Creazione del progetto
Il progetto appena creato include già al suo interno la libreria JQuery versione 1.3.2. Per comodità durante il tutorial utilizzeremo questa versione nonostante che, nel momento in cui scriviamo, non sia la più recente.
Oltre alla libreria JQuery il progetto MVC mette a disposizione un controller dedicato alla gestione degli account utente. Tuttavia, poichè nel tutorial scriveremo una versione personalizzata della pagina di registrazione, non avremo bisogno del controller predefinito: procediamo quindi ad eliminarlo insieme alle viste ad esso associate. A questo punto la struttura dell'applicazione dovrebbe essere simile all'immagine seguente:
Struttura dell'applicazione
Creare un controller e l'azione corrispondente
Iniziamo a creare il nostro form, cliccando col tasto destro del mouse sulla cartella Controller ed aggiungendo nuovo controller chiamato AccountController
:
Creazione del nuovo AccountController
All'interno del nuovo controller creiamo dunque l'azione di registrazione utente:
// Azione Register all'interno di AccountController
public class AccountController : Controller
{
public ActionResult Register()
{
return View();
}
}
Per creare la vista corrispondente all'azione Register() sarà sufficiente cliccare con il tasto destro del mouse all'interno dell'azione stessa e selezionare dal menu la voce Aggiungi Vista
:
Creazione della vista per l'azione Register
Nella finestra di dialogo che ci appare lasciamo tutti i parametri invariati.
Creazione della vista per l'azione Register
Creare un form
Occupiamoci adesso del codice per implementare la nostra view e scriviamo il markup del form vero e proprio:
<!-- Form di registrazione -->
<h2>Nuovo Account</h2>
<%= Html.ValidationSummary("La creazione dell'account non è andata a buon fine. Per favore correggi gli errori e riprova.") %>
<% using (Html.BeginForm("Register", "Account", null, FormMethod.Post, new { id = "registration"}))
{ %>
<div>
<fieldset>
<p>
<label for="username">Nome utente:</label>
<%= Html.TextBox("username") %> <%= Html.ValidationMessage("username") %>
</p>
<p>
<label for="email">Email:</label>
<%= Html.TextBox("email")%> <%= Html.ValidationMessage("email") %>
</p>
<p>
<label for="password">Password:</label>
<%= Html.Password("password")%> <%= Html.ValidationMessage("password") %>
</p>
<p>
<label for="confirmPassword">Conferma password:</label>
<%= Html.Password("confirmPassword")%> <%= Html.ValidationMessage("confirmPassword") %>
</p>
<p><input type="submit" value="Registrati!" /></p>
</fieldset>
</div>
<% } %>
Il codice del form è molto semplice e fa largo uso degli helper HTML che abbiamo già incontrato nella nostra guida ASP.NET MVC. Tuttavia è utile rivolgere l'attenzione alla direttiva Html.BeginForm
in cui assegnamo al form l'identificativo univoco registration
in modo da rendere più semplice l'integrazione con JQuery.
Torniamo adesso a lavorare sul controller, creando, nel modo in cui siamo abituati a fare con MVC, l'azione che avrà il compito di ricevere il POST dei dati contenuti nel form. Torneremo più avanti su questa azione quando parleremo di come spedire il form tramite Ajax.
// Lista di username già scelti da altri utenti e quindi non disponibili
private static List TakenUserNames = new List() { "pinco", "pallino", "pluto", "topolino", "pippo" };
// Lista di email non valide in quanto già registrate dagli utenti
private static List TakenEmails = new List() { "pico@pallino.com", "pluto@topolinia.com", "topolino@topolinia.com", "pippo@topolinia.com" };
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(string userName, string email, string password, string confirmPassword)
{
// Facciamo passare un po' di tempo per simulare i tempi di risposta del server
System.Threading.Thread.Sleep(2000);
// Esaminiamo i dati ricevuti
if (ValidateRegistration(userName, email, password, confirmPassword))
{
// I dati sono validi, registriamo l'utente
// Qui andrebbe inserito il codice di interazione con il database, ecc.
// Reindirizziamo l'utente all'homepage
return RedirectToAction("Index", "Home");
}
else
{
// I dati non sono validi, restituiamo gli errori
return View();
}
}
[NonAction]
private bool ValidateRegistration(string userName, string email, string password, string confirmPassword)
{
// Controlliamo che i campi siano validi
if (String.IsNullOrEmpty(userName))
ModelState.AddModelError("username", "Nome utente richiesto.");
if (String.IsNullOrEmpty(email))
ModelState.AddModelError("email", "Email richiesta.");
if (String.IsNullOrEmpty(password))
ModelState.AddModelError("password", "Password richiesta.");
else
if (password.Length < 5)
ModelState.AddModelError("password", "La password deve essere lunga almeno 5 caratteri.");
if (!String.Equals(password, confirmPassword, StringComparison.Ordinal))
ModelState.AddModelError("_FORM", "La password inserita non è uguale a quella di conferma.");
// Controlliamo che lo username non sia già preso
if (TakenUserNames.Contains(userName.ToLowerInvariant()))
ModelState.AddModelError("username", "Nome utente già in uso. Per favore scegline un altro.");
// Controlliamo che l'email non sia già presente ed associata ad un altro utente
if (TakenEmails.Contains(email.ToLowerInvariant()))
ModelState.AddModelError("email", "Email già in uso.");
return ModelState.IsValid;
}
Validare i dati del form
Anche se abbiamo chiesto a jQuery di verificare i campi del form, nell'azione Register
ci affidiamo ad un metodo chiamato ValidateRegistration
per effettuare la validazione dei dati inseriti dall'utente.
È sempre buona norma effettuare la validazione dei dati sul server anche quando è prevista una validazione lato client: utilizzando soltanto quest'ultima saremmo infatti esposti a diversi problemi di sicurezza; ad esempio, nel caso in cui l'utente non avesse JavaScript abilitato, rischieremmo di inserire nel nostro database dati non corretti o potenzialmente pericolosi.
L'immagine seguente mostra gli errori di validazione visualizzati dopo l'invio di dati non corretti:
Pagina con errori di validazione
La pagina che abbiamo appena creato è perfettamente funzionante su tutti i browser, anche su quelli privi di supporto a JavaScript. È importante sottolineare quindi come tutti gli utenti saranno in grado di utilizzare il nostro form di registrazione a prescindere dal browser utilizzato, mentre allo stesso tempo, secondo il principio del progressive enhancement, forniremo le funzionalità aggiuntive soltanto a coloro che hanno JavaScript ed Ajax abilitati.
Finora abbiamo gettato le basi del progetto. Nella seconda parte dell'articolo inizieremo ad implementare le funzionalità che ci siamo prefissi.
Notifica della disponibilità dello username
Veniamo adesso all'implementazione della prima funzionalità del nostro form di registrazione utente: la notifica della disponibilità dello username inserito dall'utente.
Questa funzione, ormai molto diffusa, permette di migliorare sensibilmente l'esperienza di registrazione in quanto consente di segnalare immediatamente all'utente, tramite una richiesta Ajax, se lo username da lui scelto è disponibile o meno, eliminando quindi la necessità di spedire il form ed attendere il refresh di tutta la pagina.
Cominciamo implementando all'interno del nostro controller l'azione che richiameremo tramite Ajax per controllare se lo username è disponibile: gli username già “presi” saranno contenuti in una lista statica dichiarata nel controller.
private static List<string> TakenUserNames = new List<string>() { "pinco", "pallino", "pluto", "topolino", "pippo" };
// Azione che controlla la disponibilità dello username
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult CheckUsernameAvailability(string username)
{
return Json(new { available = !TakenUserNames.Contains(username.ToLowerInvariant())});
}
L'azione restituisce una risposta Json composta da una variabile booleana, available
, che sarà vera soltanto nel caso in cui lo username sia effettivamente disponibile.
Passiamo ora all'implementazione del codice lato client che si occuperà di creare la richiesta e di notificare la risposta all'utente: nel nostro caso il controllo verrà eseguito dopo che l'utente, terminato di scrivere lo username, passa al campo di input successivo. Per sapere quando questo accade ci basterà tenere sotto controllo l'evento Blur
generato automaticamente da JavaScript quando l'utente esce da un campo di input.
Per cominciare includiamo la libreria JQuery nella vista che contiene il form di registrazione utente (Register.aspx
).
<asp:Content ID="registerContent" ContentPlaceHolderID="MainContent" runat="server">
<!--Includiamo JQuery nella pagina-->
<script src="/scripts/jquery-1.3.2.js" type="text/javascript"></script>
...
</asp:Content>
Passiamo ora al codice JavaScript vero e proprio:
<script type="text/javascript">
// Prima versione del codice JavaScript
$(document).ready(function() {
// Il campo username
var usernameInput = $("input#username");
// Attivo la funzione di verifica username
usernameInput.blur(function() {
if (usernameInput.attr("value") != "")
$.ajax({ type: "POST",
url: "/Account/CheckUsernameAvailability",
data: usernameInput.serialize(),
dataType: "json",
success: function(msg) {
if (msg.available) alert("Nome utente disponibile.");
else alert("Nome utente non disponibile. Per favore scegline un altro.");
}
});
});
});
</script>
Nel codice precedente selezioniamo il campo di input con id username
e registriamo una funzione all'evento blur
.
Nella funzione controlliamo prima di tutto che l'utente abbia effettivamente scritto qualcosa all'interno del campo per evitare di effettuare una richiesta inutile al server, per poi passare alla creazione della richiesta Ajax vera e propria. Come URL utilizziamo quello dell'azione creata poco fa, a cui inviamo come POST
il contenuto del campo username. Nel caso in cui la richiesta vada a buon fine mostriamo all'utente un messaggio per informarlo della disponibilità del nome utente da lui scelto. Ecco il risultato:
Risultato del primo script di verifica username
Questo approccio, per quanto completamente funzionante, è tuttavia un po' spartano. Vediamo quindi di migliorarlo, ad esempio mostrando all'utente un messaggio di notifica un po' più elaborato, inserito direttamente nella pagina accanto al campo di input.
Per modificare le notifiche avremo bisogno, per prima cosa, modifichiamo il codice del form aggiungendo un tag <span>
in cui visualizzare il risultato della richiesta Ajax, posizionato in prossimità del campo username.
Il nuovo codice per il campo username:
<!-- ... -->
<p>
<label for="username">Nome utente:</label>
<%= Html.TextBox("username") %>
<%= Html.ValidationMessage("username") %>
<span id="usernamecheck"></span>
</p>
<!-- ... -->
Fatto questo modifichiamo il codice JavaScript scritto in precedenza per includere la nuova funzionalità:
Nel codice precedente abbiamo eliminato l'alert di conferma ed utilizzato lo span
per fornire la notifica all'utente. Abbiamo anche reso lo script iniziale più efficiente dal punto di vista delle performance ottimizzando il codice di selezione dei tag, la cui ricerca avviene adesso soltanto all'interno del form e non in tutta la pagina. Inoltre salviamo l'ultimo username verificato nella variabile lastCheckedUsername in modo da eseguire la richiesta Ajax soltanto nel caso in cui i dati siano stati modificati dall'ultimo controllo effettuato. Ecco quindi il risultato:
Risultato del secondo script di verifica username
Validazione lato client
Per la validazione lato client faremo affidamento sull'ottimo plugin gratuito JQuery Validation.
Dopo aver scaricato il plugin dalla pagina ufficiale è sufficiente copiare nella cartella Scripts
dell'applicazione il file jquery.validate.js
ed includerlo nella vista Register.aspx
, ecco il codice per l'inclusione:
<script src="/scripts/jquery.validate.js" type="text/javascript"></script>
Cominciamo quindi la scrittura del codice JavaScript. Ecco la prima versione dello script di validazione:
<script type="text/javascript">
$(document).ready(function() {
// Il nostro form
var regForm = $("#registration");
// Codice per verificare la disponibilità dello username
...
// Codice per la validazione lato client
regForm.validate({
// Regole di validazione
rules: {
username: { required: true },
password: { required: true, minlength: 5 },
confirmPassword: { required: true, minlength: 5, equalTo: "#password" },
email: { required: true, email: true }
}
});
});
</script>
Utilizziamo il metodo validate sul nostro form per attivare le funzionalità di validazione lato client. Il metodo accetta un oggetto contenente i parametri di inizializzazione. Nel nostro caso abbiamo specificato soltanto il parametro rules
, che contiene le condizioni da soddisfare per considerare corretto l'input dell'utente.
Le regole di validazione sono definite all'interno di rules
tramite questa sintassi:
Sintassi per la definizione di regole di validazione
idCampo: { condizioni }
Nel codice precedente abbiamo fatto uso delle seguenti condizioni:
Condizione | Valori | Descrizione |
---|---|---|
required |
true/false |
per rendere il campo obbligatorio o meno |
minlength |
numero_minimo_caratteri |
per richiedere l'inserimento di un numero minimo di caratteri |
email |
true/false |
per validare il testo inserito come indirizzo email |
equalTo |
"#idCampo" |
er richiedere che il contenuto del campo attuale sia uguale a quello del campo specificato |
Per una lista completa delle regole e delle condizioni utilizzabili è possibile consultare la documentazione ufficiale di JQuery Validation.
Risultato della validazione lato client
Possiamo anche personalizzare i messaggi di errore in modo da renderli più user friendly utilizzando il parametro di inizializzazione messages
:
regForm.validate({
rules: { /* Regole di validazione */ },
// Messaggi di errore personalizzati
messages:
{
username: "Nome utente richiesto.",
password: { required: "Password richiesta.",
minlength: "La password deve essere lunga almeno 5 caratteri." },
confirmPassword: { required: "Password richiesta.",
minlength: "La password deve essere lunga almeno 5 caratteri.",
equalTo: "La password inserita non è uguale a quella di conferma."},
email: { required: "Email richiesta.", email: "L'email inserita non è valida." }
},
});
La sintassi per definire messaggi di errore personalizzati è praticamente identica a quella utilizzata per definire le condizioni di validazione. Ed ecco il risultato finale:
Validazione lato client con errori personalizzati
Spedizione del form tramite Ajax
Per aggiungere l'invio del form basato su Ajax sono sufficienti alcune piccole modifiche al codice del controller e del form. Cominciamo occupandoci del controller, in particolare dell'azione Register:
// Nuovo codice per l'azione Register
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(string userName, string email, string password, string confirmPassword)
{
// Facciamo passare un po' di tempo per simulare i tempi di risposta del server
System.Threading.Thread.Sleep(2000);
// Esaminiamo i dati ricevuti
if (ValidateRegistration(userName, email, password, confirmPassword))
{
// I dati sono validi, registriamo l'utente
if (Request.IsAjaxRequest())
{
// Nel caso di una richiesta Ajax restituiamo un oggetto
// Json con il risultato
return Json(new { success = true });
}
else
{
// Altrimenti ci accontentiamo di reindirizzare
// l'utente all'homepage
return RedirectToAction("Index", "Home");
}
}
else
{
// I dati non sono validi, restituiamo gli errori
if (Request.IsAjaxRequest())
{
// Nel caso di una richiesta Ajax restituiamo un oggetto
// Json con il risultato
List err = new List();
foreach (var item in ModelState) {
foreach (var e in item.Value.Errors) {
err.Add(e.ErrorMessage);
}
}
return Json(new { success = false, errors = err });
}
else
{
// Altrimenti visualizziamo nuovamente tutto il form return View();
}
}
}
Il codice non è molto diverso da quello visto all'inizio del tutorial: abbiamo aggiunto soltanto delle condizioni sulla richiesta ricevuta dalla nostra azione. Nel caso in cui questa sia di tipo Ajax verrà restituito un oggetto JSON contenente il risultato dell'operazione di registrazione utente, assieme alla lista degli errori nel caso in cui la registrazione non sia andata a buon fine. Nel caso di una richiesta normale (non Ajax) continueremo a restituire direttamente la vista. Non sono necessarie altre modifiche lato server.
Cominciamo quindi a lavorare sulla vista Register.aspx
aggiungendo, appena sopra il form, un contenitore costituito da un div
vuoto che servirà a mostrare il risultato della richiesta Ajax.
<h2>Nuovo Account</h2>
<div id="ajaxresult"></div>
...
Per quanto riguarda il codice JavaScript, per abilitare la spedizione del form tramite Ajax ci serviremo del plugin gratuito JQuery Form. Scarichiamo quindi il plugin ed inseriamo il file jquery.form.js nella cartella Scripts della nostra applicazione.
Per brevità non sarà possibile fornire una spiegazione approfondita delle funzionalità offerte dal plugin e ci limiteremo ad illustrare i metodi di nostro interesse. Informazioni approfondite corredate da esempi pratici sono disponibili sul sito del plugin.
Includiamo il file jquery.form.js nella vista Register.aspx e procediamo alla scrittura del codice necessario:
<script type="text/javascript">
$(document).ready(function() {
// Il nostro form
var regForm = $("#registration");
// Il div che conterrà il risultato della richiesta ajax
var resultField = $("#ajaxresult");
// Codice per verificare la disponibilità dello username
// Codice per la validazione lato client
// e per la spedizione Ajax del form
regForm.validate({
rules: { /* Regole di validazione */},
messages: { /* Messaggi di errore personalizzati */},
// Spedizione tramite Ajax
submitHandler: function(form) {
// Blocco il pulsante di invio per evitare invii multipli del form
$("input[type=submit]", regForm).attr("disabled", "disabled");
// Elimino i vecchi errori
resultField.html("");
resultField.removeClass("validation-summary-errors");
$(form).ajaxSubmit({
// La seguente funzione viene richiamata automaticamente
// nel caso in cui la richiesta vada a buon fine
success: function(result) {
// Controllo il risultato e mostro gli eventuali messaggi
if (result.success) {
// La registrazione è avvenuta con successo
// Nascondo il form...
regForm.hide("slow");
// ...e mostro un messaggio di conferma
resultField.text("Registrazione avvenuta con successo!");
} else {
// Ci sono degli errori
resultField.html('<span class="validation-summary-errors">La creazione dell'account non è andata a buon fine. Per favore correggi gli errori e riprova.</span>');
if (result.errors) {
var errorsHtml = '<ul class="validation-summary-errors">';
for (var error in result.errors) {
errorsHtml += "<li>" + result.errors[error] + "</li>";
}
errorsHtml += "</ul>";
resultField.append(errorsHtml);
}
// Attivo il pulsante di invio del form $("input[type=submit]", regForm).removeAttr("disabled");
};
},
// Specifico che la risposta del server conterrà
// un oggetto json dataType:
"json"
});
}
});
});
</script>
Nel listato abbiamo inserito il codice per la spedizione Ajax direttamente all'interno di quello legato alla validazione. Facciamo uso infatti del callback submitHandler
per “intercettare” l'invio del form e sostituire al normale POST
della pagina la nostra richiesta Ajax. In questo modo saremo anche certi che la spedizione avverrà soltanto nel caso in cui il form sia valido rispetto alle regole di validazione definite in precedenza.
Il plugin JQuery Form mette a disposizione il metodo ajaxSubmit
per spedire il form tramite Ajax. Il metodo accetta un oggetto contenente i parametri di inizializzazione: nel nostro caso utilizziamo il callback success
per le operazioni successive alla ricezione della risposta dal server. Il codice è molto semplice e vi invito a leggere i commenti per un'analisi più approdondita. Molto importante è il parametro dataType
che rappresenta il tipo di dato restituito dal server all'interrogazione Ajax, nel nostro caso un oggetto JSON.
Ecco dunque il risultato nel caso in cui non si siano verificati errori nella validazione o nella spedizione del form:
Spedizione Ajax del form eseguita con successo