Skip to content

5. La vista e il suo modello

5.1. Introduzione

Torniamo all'architettura di un'applicazione ASP.NET MVC:

Nel capitolo precedente abbiamo esaminato come ASP.NET MVC presenti le informazioni della richiesta [1] a un'azione [2a] sotto forma di un modello che può contenere vincoli di validazione. Questo modello è stato passato come input all'azione e lo abbiamo definito modello dell'azione. Ora ci concentreremo sul risultato più comune di un'azione, il tipo ViewResult [ ], che corrisponde a una vista V [3] accompagnata dal suo modello M [2c]. Questo modello sarà chiamato modello di vista V, da non confondere con il modello di azione che abbiamo appena studiato. Uno è l'input dell'azione, l'altro è l'output.

Iniziamo creando un nuovo progetto [Example-03] [1] all'interno della stessa soluzione, del tipo ASP.NET MVC di base:

Creiamo un controller denominato [First] [2]. Il codice generato per questo controller è il seguente:


using System.Web.Mvc;
 
namespace Exemple_03.Controllers
{
  public class FirstController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
 
  }
}

  • righe 7–10: è stata creata un'azione [Index]. Il tipo di ritorno del metodo [Index] è quello della classe [ActionResult], da cui derivano la maggior parte dei possibili risultati delle azioni;
  • riga 9: il metodo [View] della classe [Controller] (riga 5) restituisce un tipo [ViewResult], che deriva da [ActionResult]. Questo metodo supporta numerosi sovraccarichi. Ne esamineremo alcuni. Il principale è il seguente:

Image

  • il primo parametro è il nome della vista. Se viene omesso, la vista utilizzata è quella con lo stesso nome dell'azione che produce il [ViewResult], e verrà cercata nella cartella [/Views/{controller}], dove {controller} è il nome del controller;
  • il secondo è il modello della vista. Se viene omesso, la vista non ha alcun modello.

Il metodo [Index] riportato di seguito:


public ActionResult Index()
    {
      return View();
}

richiede la visualizzazione della vista [ /Views/First/Index.cshtml ]. Non le passa alcun modello. Creiamo [1] la cartella [/Views/First]:

quindi creiamo la vista [Index] [2] al suo interno:

Specifichiamo il nome della vista in [3]. Viene creata in [4]. Il codice generato è il seguente:


@{
    Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
 
    </div>
</body>
</html>

Si tratta di HTML standard, ad eccezione delle righe 1-3, che sono codice C#. Il programma che gestisce le viste è chiamato motore di viste. Gestisce tutto ciò che non è HTML e lo converte in HTML. In definitiva, questo è ciò che verrà inviato al client. Il motore di viste utilizzato qui è [Razor]. Consente di includere codice C# all'interno di una vista. [Razor] interpreta questo codice C# e genera HTML da esso. Ecco alcune regole di base per includere codice C# in una vista:

  • La transizione da HTML a C# avviene quando si incontra il carattere @ (riga 1). Se questo carattere introduce un blocco di codice, si utilizzano le parentesi graffe (righe 1 e 3). Se introduce una variabile di cui si desidera recuperare il valore, è sufficiente scrivere @variable;
  • La transizione da C# a HTML avviene quando si incontra il carattere < (riga 5). A volte, potrebbe essere necessario forzare questa transizione, in particolare quando si include testo semplice senza tag HTML nella pagina. In tal caso, utilizzare il tag <text> per inserire il testo: <text>testo semplice qui</text>.

La riga 2 sopra indica che la vista [Index] non ha una pagina master.

Modifichiamo la vista come segue:


@{
  Layout = null;
  string vue = "Index";
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Index</title>
</head>
<body>
  <div>
    <h3>Vue @vue</h3>
  </div>
</body>
</html>

  • Riga 3: definisce una variabile C#;
  • riga 15: visualizza il valore di questa variabile.

Ora richiediamo l'URL [/First/Index]:

Image

Il codice HTML ricevuto è il seguente:

<!DOCTYPE html>

<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Index</title>
</head>
<body>
  <div>
    <h3>Vue Index</h3>
  </div>
</body>
</html>

Questo è un documento HTML puro. Tutto il codice C# è stato rimosso.

5.2. Utilizzo di [ViewBag] per passare informazioni alla vista

Creiamo una nuova azione denominata [Action01] associata alla vista [Action01.cshtml]:

L'azione [Action01] è la seguente:


    // Action01
    public ViewResult Action01()
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return View();
}

  • Riga 4: utilizziamo la proprietà [ViewBag] del controller. Si tratta di un oggetto dinamico a cui è possibile aggiungere proprietà, come mostrato alla riga 4. Una caratteristica fondamentale di questo oggetto è che è accessibile anche alla vista. Rappresenta quindi un modo per passare informazioni alla vista;
  • riga 5: viene richiesta la vista predefinita per l'azione. Si tratta della vista [/First/Action01.cshtml]. Non le viene passato alcun modello.

La vista [Action01.cshtml] è la seguente:


@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action01</title>
</head>
<body>
  <div>
    <h4>@ViewBag.info</h4>
  </div>
</body>
</html>

  • Riga 14: Viene visualizzata la proprietà [ViewBag.info].

Proviamola. Richiediamo l'URL [/First/Action01]:

Image

5.3. Utilizzo di un modello fortemente tipizzato per passare informazioni alla vista

Il metodo precedente presenta l'inconveniente di non consentire il rilevamento degli errori prima dell'esecuzione. Pertanto, se la vista [Action01.cshtml] utilizza il codice


<h4>@ViewBag.Info</h4>

si verificherà un errore perché la proprietà [Info] non esiste. Quella creata dall'azione [Action01] si chiama [info]. Possiamo quindi utilizzare un modello fortemente tipizzato per evitare questo problema.

In uno degli esempi discussi in precedenza, l'azione era la seguente:


    // Action10
    public ContentResult Action10(ActionModel03 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("email={0}, jour={1}, info1={2}, info2={3}, info3={4}, erreurs={5}",
        modèle.Email, modèle.Jour, modèle.Info1, modèle.Info2, modèle.Info3, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

L'azione [Action10] passava sei informazioni (Email, Day, Info1, Info2, Info3, errors) al proprio client sotto forma di stringa. Passeremo queste informazioni a un modello di vista [ViewModel01]. Poiché questo modello utilizza informazioni provenienti da [ActionModel03], lo deriviamo da quella classe.

Iniziamo copiando [ActionModel03] dal progetto [Example-02] nel progetto corrente [Example-03]:

e ne modifichiamo lo spazio dei nomi in modo che corrisponda a quello del progetto [Example-03]:


using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
  public class ActionModel03
  {
    [Required(ErrorMessage = "Le paramètre email est requis")]
    [EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
    public string Email { get; set; }
 
    [Required(ErrorMessage = "Le paramètre jour est requis")]
    [RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
    public string Jour { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info1 est requis")]
    [MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
    public string Info1 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info2 est requis")]
    [MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
    public string Info2 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info3 est requis")]
    [MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    [MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    public string Info3 { get; set; }
  }
}

  • riga 2: il nuovo spazio dei nomi;

Quindi creiamo la classe [ViewModel01]:

Il codice per [ViewModel01] è il seguente:


namespace Exemple_03.Models
{
  public class ViewModel01 : ActionModel03
  {
    public string Erreurs { get; set; }
  }
}

  • Riga 3: la classe eredita da [ActionModel03] e quindi le proprietà [Email, Day, Info1, Info2, Info3];
  • riga 5: aggiungiamo la proprietà [Errors].

Ora scriviamo l'azione [Action02] che:

  • accetta il modello di azione [ActionModel03] come input;
  • e restituisce il modello di vista [ViewModel01].

Il codice è il seguente:


    // Action02
    public ViewResult Action02(ActionModel03 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      return View(new ViewModel01(){Email=modèle.Email, Jour=modèle.Jour, Info1=modèle.Info1, Info2=modèle.Info2, Info3=modèle.Info3, Erreurs=erreurs});
}

  • riga 1: [Action02] riceve il modello di azione [ActionModel03]. Restituisce un risultato di tipo [ViewResult];
  • riga 4: gli errori relativi al modello di azione [ActionModel03] vengono aggregati nella stringa [errors]. Il metodo [getErrorMessagesFor] è stato descritto a pagina 60 ed è stato incluso nel controller [First] del nuovo progetto;
  • riga 5: il metodo [View] viene chiamato con un parametro. Si tratta del modello di vista. La vista stessa non è specificata. Pertanto, verrà utilizzata la vista predefinita [/Views/First/Action02]. Il modello di vista [ViewModel01] viene istanziato e inizializzato con le cinque informazioni provenienti dal modello di azione [ActionModel03] e le informazioni [errors] costruite alla riga 4.

Ora creiamo la vista [/First/Action02.cshtml]:

Il codice è il seguente:


@model Exemple_03.Models.ViewModel01
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action02</title>
</head>
<body>
  <h3>Informations du modèle de vue</h3>
  <ul>
    <li>Email : @Model.Email</li>
    <li>Jour : @Model.Jour</li>
    <li>Info1 : @Model.Info1</li>
    <li>Info2 : @Model.Info2</li>
    <li>Info3 : @Model.Info3</li>
    <li>Erreurs : @Model.Erreurs</li>
  </ul>
</body>
</html>

  • La nuova funzionalità si trova alla riga 1. La notazione [@model] imposta il tipo del modello di visualizzazione. Questo modello viene quindi referenziato dalla notazione [@Model] (righe 16–21);
  • Righe 15–22: Le informazioni del modello vengono visualizzate in un elenco.

Vediamo alcuni esempi di esecuzione dell'azione [Action02].

Innanzitutto, senza parametri:

Image

poi con parametri errati:

Image

poi con parametri corretti:

Image

In questo esempio, il modello di vista [ViewModel01] utilizza le informazioni del modello di azione [ActionModel03]. Questo accade spesso. Possiamo quindi utilizzare un unico modello che funga sia da modello di azione che da modello di vista. Creiamo un nuovo modello [ActionModel04]:

Image

che sarà il seguente:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_03.Models
{
  [Bind(Exclude="Erreurs")]
  public class ActionModel04
  {
    // ---------------------- Action --------------------------------
    [Required(ErrorMessage = "Le paramètre email est requis")]
    [EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
    public string Email { get; set; }
 
    [Required(ErrorMessage = "Le paramètre jour est requis")]
    [RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
    public string Jour { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info1 est requis")]
    [MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
    public string Info1 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info2 est requis")]
    [MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
    public string Info2 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info3 est requis")]
    [MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    [MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    public string Info3 { get; set; }
 
    // ---------------------- view --------------------------------
    public string Erreurs { get; set; }
  }
}

  • righe 8–28: il modello di azione con i suoi vincoli di integrità. Questi campi faranno parte anche della vista;
  • riga 31: una proprietà specifica del modello di vista. È stata esclusa dal modello di azione dall'annotazione alla riga 5.

Creiamo la seguente nuova azione [Action03]:


    // Action03
    public ViewResult Action03(ActionModel04 modèle)
    {
      modèle.Erreurs = getErrorMessagesFor(ModelState);
      return View(modèle);
}

  • riga 2: [Action03] riceve il modello di azione di tipo [ActionModel04];
  • riga 5: e restituisce questo stesso modello come modello di vista;
  • riga 4: integrato con le informazioni [Errors];

Non resta che creare la vista [/First/Action03.cshtml]:

  • in [1]: clicca con il tasto destro del mouse sul codice [Action03], quindi seleziona [Aggiungi vista];
  • in [2]: il nome predefinito della vista;
  • in [3]: specificare che si sta creando una vista fortemente tipizzata;
  • in [4]: selezionare la classe corretta dall'elenco a discesa, in questo caso la classe [ActionModel04];
  • in [5]: la vista creata.

Assegniamo alla vista [Action03] lo stesso codice della vista [Action02]. Cambiano solo il modello di vista (riga 1) e il titolo della pagina (riga 11):


@model Exemple_03.Models.ActionModel04
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action03</title>
</head>
<body>
  <h3>Informations du modèle de vue</h3>
  <ul>
    <li>Email : @Model.Email</li>
    <li>Jour : @Model.Jour</li>
    <li>Info1 : @Model.Info1</li>
    <li>Info2 : @Model.Info2</li>
    <li>Info3 : @Model.Info3</li>
    <li>Erreurs : @Model.Erreurs</li>
  </ul>
</body>
</html>

Ora chiamiamo l'azione [Action03] senza alcun parametro:

Image

I risultati sono gli stessi di prima. È prassi comune utilizzare lo stesso modello sia per l'azione che per la vista, poiché il modello della vista spesso riutilizza le informazioni del modello dell'azione. Utilizziamo quindi un modello più ampio che possa essere impiegato sia dall'azione che dalla vista che essa genera. Dobbiamo fare attenzione a escludere dal binding dei dati le informazioni che non appartengono al modello dell'azione. In caso contrario, un utente esperto potrebbe inizializzare parti del modello della vista a nostra insaputa.

5.4. [Razor] – Introduzione

Ora introdurremo alcuni elementi delle viste [Razor], principalmente le istruzioni foreach e if.

Supponiamo di voler visualizzare un elenco di persone in una tabella HTML. Il modello di vista potrebbe essere il seguente [ViewModel02]:


namespace Exemple_03.Models
{
  public class ViewModel02
  {
    public Personne[] Personnes { get; set; }
    public ViewModel02()
    {
      Personnes = new Personne[] { new Personne { Nom = "Pierre", Age = 44 }, new Personne { Nom = "Pauline", Age = 12 } };
    }
  }
 
  public class Personne
  {
    public string Nom { get; set; }
    public int Age { get; set; }
  }
}
  • La vista del modello è la classe [ViewModel02], righe 3–10;
  • riga 5: il modello ha un array di persone di tipo [Person] definito alle righe 12–16;
  • righe 6–10: il costruttore del modello inizializza la proprietà [People] della riga 5 con un array di due persone.

L'azione che produce questo modello come output sarà la seguente [Action04]:


    // Action04
    public ViewResult Action04()
    {
      return View(new ViewModel02());
}
  • riga 2: l'azione non ha un modello di input;
  • riga 4: passa la sua vista predefinita, un'istanza del modello [ViewModel02] che abbiamo appena definito.

La vista [Action04.cshtml] visualizzerà il modello [ViewModel02]:

Il codice della vista [Action04.cshtml] è il seguente:


@model Exemple_03.Models.ViewModel02
@using Exemple_03.Models
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action04</title>
</head>
<body>
  <table border="1">
    <thead>
      <tr>
        <th>Nom</th>
        <th>Age</th>
      </tr>
    </thead>
    <tbody>
      @foreach (Personne p in Model.Personnes)
      {
        <tr>
          <td>@p.Nom</td>
          <td>@p.Age</td>
        </tr>
      }
    </tbody>
  </table>
</body>
</html>

  • riga 1: il modello di visualizzazione;
  • riga 2: importazione dello spazio dei nomi per la classe [Person] utilizzata alla riga 24;
  • righe 16–32: la tabella HTML che visualizza le persone del modello;
  • Riga 24: l'inizio del codice C# è contrassegnato dal carattere @. L'istruzione [foreach] eseguirà un ciclo su tutte le persone presenti nel modello;
  • righe 26–27: il carattere < conclude il codice C# e dà inizio all'HTML. Successivamente, il carattere @ ricompare per tornare al C# e scrivere il nome della persona. Quindi, il carattere < ricompare per tornare alla modalità HTML;
  • Riga 28: viene scritta l'età della persona.

L'esecuzione dell'azione [Action04] produce il seguente risultato:

Image

Altri elementi di una vista possono essere popolati da una raccolta: elenchi (a discesa o di altro tipo), pulsanti di opzione e caselle di controllo. Si consideri il seguente nuovo esempio, che visualizza un elenco a discesa.

Il modello [ViewModel05] sarà il seguente:


namespace Exemple_03.Models
{
  public class ViewModel05
  {
    public Personne2[] Personnes { get; set; }
    public int SelectedId { get; set; }
 
    public ViewModel05()
    {
      Personnes = new Personne2[] { 
        new Personne2 { Id = 1, Prénom = "Pierre", Nom = "Martino" }, 
        new Personne2 { Id = 2, Prénom = "Pauline", Nom = "Pereiro" }, 
        new Personne2 { Id = 3, Prénom = "Jacques", Nom = "Alfonso" } };
      SelectedId = 2;
    }
  }
 
  public class Personne2
  {
    public int Id { get; set; }
    public string Nom { get; set; }
    public string Prénom { get; set; }
  }
}

  • riga 18: una classe [Person2] con tre proprietà;
  • riga 3: il modello [ViewModel05] per la vista;
  • riga 5: l'elenco delle persone da visualizzare nell'elenco a discesa nel formato [Nome Cognome];
  • riga 6: l'[Id] della persona da selezionare dall'elenco a discesa;
  • righe 8–16: il costruttore che crea un array di tre persone (righe 10–13) e imposta l'[Id] della persona che dovrebbe apparire selezionata.

La vista [Action05.cshtml] visualizzerà questo modello:

Il suo codice è il seguente:


@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action05</title>
</head>
<body>
  <select>
    @foreach (Personne2 p in Model.Personnes)
    {
      string selected = "";
      if (p.Id == Model.SelectedId)
      {
        selected = "selected=\"selected\"";
      }
      <option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
    }
  </select>
</body>
</html>

Le caratteristiche dell'elenco a discesa HTML sono state illustrate nella sezione 2.5.2.6. Rivediamole:

Combo
<select size="1" name="cmbValues">
<option value="1">opzione1</option>
<option selected="selected" value="2">opzione2</option>
<option value="3">Opzione 3</option>
</select>

Image

Tag HTML
<select size=".." name="..">
<option [selected="selected"] value=”v”>...</option>
...
</select>
visualizza il testo tra i tag <option>...</option> in un elenco
attributi
name="cmbValeurs": nome del controllo.
size="1": numero di voci visibili dell'elenco. size="1" rende l'elenco equivalente a una casella combinata.
selected="selected": se questa parola chiave è presente per una voce dell'elenco, tale voce appare selezionata nell'elenco. Nel nostro esempio sopra, la voce dell'elenco choice2 appare come voce selezionata nella casella combinata quando viene visualizzata per la prima volta.
value=”v”: se la voce viene selezionata dall’utente, questo valore [v] viene inviato al server. Se questo attributo è assente, il testo visualizzato e selezionato viene inviato al server.

Il codice nelle righe 17–25 genera i tag <option> che vengono inseriti all'interno del tag <select> alla riga 16.

  • riga 17: si esegue un ciclo sull'elenco delle persone nel modello;
  • riga 20: controlliamo se la persona corrente è quella da selezionare. In tal caso, prepariamo il testo selected="selected" da inserire nel tag <option>;
  • Riga 24: viene scritto il tag <option>.

Chiamiamo l'azione [Action05]:

  • in [1,2], le persone vengono visualizzate nel formato [Nome Cognome];
  • in [1,2], la persona selezionata è quella con un [Id] pari a 2.

Esaminiamo ora il codice sorgente HTML della pagina sopra riportata:


<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action05</title>
</head>
<body>
  <select>
      <option value="1" >Pierre Martino</option>
      <option value="2" selected=&quot;selected&quot;>Pauline Pereiro</option>
      <option value="3" >Jacques Alfonso</option>
  </select>
</body>
</html>

  • righe 10–12: i tre tag <option> generati dal codice [Razor];
  • riga 11: la persona con [Id]=2 è stata effettivamente selezionata.

I due esempi sopra riportati sono sufficienti. Quando si scrive una vista [Razor], è necessario resistere alla tentazione di inserirvi della logica. Il codice C# ci consentirebbe di farlo. Tuttavia, nel modello MVC, la logica deve trovarsi nell'azione o nei livelli inferiori [Logica di business, DAO], ma non nella vista. Anche quando si aderisce al modello MVC, ci si può ritrovare con molta logica nella vista per calcolare valori intermedi. Ciò può significare che il modello utilizzato non è abbastanza dettagliato. Il modello deve contenere i valori finali di cui la vista ha bisogno, in modo che non debba calcolarli da sola. Una buona vista è quella con una logica minima e una struttura HTML chiara. Se viene inserito troppo codice C#, la struttura HTML può diventare illeggibile.

Nell'esempio sopra riportato, l'elenco a discesa potrebbe essere utilizzato da un utente e noi vorremmo quindi sapere quale persona ha selezionato. Per questo, abbiamo bisogno di un modulo.

5.5. Moduli – Introduzione

Il modulo presentato all'utente sarà il seguente:

Image

Il modello di visualizzazione sarà il modello [ViewModel05] utilizzato in precedenza. L'azione che visualizzerà questa vista sarà la seguente:


    // Action06-GET
    [HttpGet]
    public ViewResult Action06()
    {
      return View("Action06Get",new ViewModel05());
}

  • Riga 2: L'azione può essere richiesta solo tramite una richiesta HTTP GET;
  • riga 5: la vista [/First/Action06Get.cshtml] verrà visualizzata utilizzando un'istanza di tipo [ViewModel05] come modello.

La vista [/First/Action06Get.cshtml] sarà la seguente:


@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action06-GET</title>
</head>
<body>
  <h3>Action06 - GET</h3>
  <p>Choisissez une personne</p>
  <form method="post" action="/First/Action06">
    <select name="personneId">
      @foreach (Personne2 p in Model.Personnes)
      {
        string selected = "";
        if (p.Id == Model.SelectedId)
        {
          selected = "selected=\"selected\"";
        }
        <option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
      }
    </select>
    <input name="valider" type="submit" value="Valider" />
  </form>
</body>
</html>

Le principali novità sono le seguenti:

  • riga 18: Affinché il browser possa trasmettere le informazioni inserite da un utente, è necessario un modulo. Esso è delimitato dal tag <form> alle righe 18 e 31.

Il tag HTML <form> è stato introdotto nella sezione 2.5.2.1. Rivediamo le sue caratteristiche:

form

<form method="post" action="FormulairePost.aspx">
Tag HTML
<form name="..." method="..." action="...">...</form>
attributi
name="frmexample": nome del modulo - facoltativo
method="..." : metodo utilizzato dal browser per inviare i valori raccolti nel modulo al server web
action="..." : URL a cui verranno inviati i valori raccolti nel modulo.
Un modulo web è racchiuso tra i tag <form>...</form>. Il modulo può avere un nome (name="xx"). Ciò vale per tutti i controlli presenti all'interno di un modulo. Lo scopo di un modulo è quello di raccogliere le informazioni inserite dall'utente tramite la tastiera o il mouse e inviarle a un URL del server web. Quale? Quello indicato nell'attributo action="URL". Se questo attributo manca, le informazioni saranno inviate all'URL del documento contenente il modulo. Un client web può utilizzare due diversi metodi, chiamati POST e GET, per inviare dati a un server web. L'attributo method="method", dove method è impostato su GET o POST, nel tag <form> indica al browser quale metodo utilizzare per inviare le informazioni raccolte nel modulo all'URL specificato dall'attributo action="URL". Quando l'attributo method non è specificato, viene utilizzato per impostazione predefinita il metodo GET.
  • riga 18: vediamo che i valori del modulo saranno inviati all'URL [/First/Action06] tramite una richiesta HTTP POST;
  • Riga 30: un modulo deve avere un pulsante [submit]. Questo pulsante attiva l'invio dei valori inseriti all'URL specificato dall'attributo [action] del tag <form>.

Cosa invierà esattamente il browser quando l'utente clicca sul pulsante [Submit]? Questo è stato spiegato nella sezione 2.5.3.1. Ricapitoliamo quanto detto:


Controllo HTML


valore


valori restituiti

<input type="radio" value="Sì" name="R1"/>Sì
<input type="radio" name="R1" value="No" checked="checked"/>No
R1=Sì
- il valore dell'attributo value del pulsante di opzione selezionato dall'utente.
<input type="checkbox" name="C1" value="uno"/>1
<input type="checkbox" name="C2" value="due" checked="checked"/>2
<input type="checkbox" name="C3" value="three"/>3
C1=uno
C2=due
- valori degli attributi value delle caselle di controllo selezionate dall'utente
<input type="text" name="txtInput" size="20" value="alcune parole"/>
txtInput=programmazione+web
- testo digitato dall'utente nel campo di immissione. Gli spazi sono stati sostituiti dal segno +
<input type="password" name="txtMdp" size="20" value="aPassword"/>
txtPassword=thisIsSecret
- testo digitato dall'utente nel campo di immissione
<textarea rows="2" name="inputArea" cols="20">
riga1
riga2
riga3
</textarea>
campo_input=le+nozioni+di+base+del+Web%0D%0A
programmazione+web
- testo digitato dall'utente nel campo di immissione. %OD%OA è il marcatore di fine riga. Gli spazi sono stati sostituiti dal segno +
<select size="1" name="cmbValues">
<option value='1'>scelta1</option>
<option selected="selected" value='2'>scelta2</option>
<option value='3'>scelta3</option>
</select>
cmbValues=3
- L'attributo [value] dell'elemento selezionato dall'utente
<select size="3" name="lst1">
<option selected="selected" value='1'>list1</option>
<option value='2'>list2</option>
<option value='3'>list3</option>
<option value='4'>list4</option>
<option value='5'>elenco5</option>
</select>
lst1=3
- attributo [value] dell'elemento selezionato dall'utente
<select size="3" name="lst2" multiple="multiple">
<option selected="selected" value='1'>list1</option>
<option value='2'>list2</option>
<option selected="selected" value='3'>list3</option>
<option value='4'>list4</option>
<option value='5'>list5</option>
</select>
lst2=1
lst2=3
- attributi [value] degli elementi selezionati dall'utente
<input type="submit" value="Invia" name="cmdSubmit"/>
 
cmdRenvoyer=Invia
- attributi name e value del pulsante utilizzato per inviare i dati del modulo al server
<input type="hidden" name="secret" value="aValue"/>
 
secret=aValue
- attributo value del campo nascosto

Nel nostro modulo, abbiamo due tag che possono inviare un valore:


    <select name="personneId">
...
</select>

e


<input name="valider" type="submit" value="Valider" />

Se l'utente seleziona la persona n. 2, i valori verranno inviati nel seguente formato:

personneId=2&valider=Valider

I nomi dei parametri corrispondono agli attributi [name] dei tag coinvolti nel POST. Senza questo attributo, i tag non inviano alcun valore. Pertanto, nell'esempio sopra riportato, potremmo omettere l'attributo name="valider" dal pulsante [submit]. Il valore inviato è l'attributo [value] del pulsante. In questo caso, tale informazione non è rilevante per noi. A volte i moduli hanno più pulsanti [submit]. In questi casi, è importante sapere quale pulsante è stato cliccato. Assegneremo quindi l'attributo [name] ai diversi pulsanti.

Il tag <select> è costituito da una serie di tag <option>:


    <select name="personneId">
        <option value="1" >Pierre Martino</option>
        <option value="2" selected=&quot;selected&quot;>Pauline Pereiro</option>
        <option value="3" >Jacques Alfonso</option>
</select>

Viene inviato il valore dell'attributo [value] dell'opzione selezionata. Se questo attributo manca, viene inviato il testo visualizzato dall'opzione, ad esempio [Pierre Martino].

La stringa

personneId=2&valider=Valider

verrà inviata al seguente URL [/First/Action06]:


    // Action06-POST
    [HttpPost]
    public ViewResult Action06(ActionModel06 modèle)
    {
      return View("Action06Post",modèle);
}

Ricorderete che avevamo già un'azione [Action06]:


    // Action06-GET
    [HttpGet]
    public ViewResult Action06()
    {
      return View("Action06Get",new ViewModel05());
}

È possibile avere due azioni con lo stesso nome, purché non gestiscano le stesse richieste HTTP:

  • [Action06] alla riga 3 gestisce una POST (riga 2);
  • [Action06] alla riga c gestisce un GET (riga b).

L'azione [Action06] che gestisce la richiesta POST riceverà la seguente stringa di parametri:

personneId=2&valider=Valider

Abbiamo bisogno di un modello di azione per incapsulare questi valori. Questo sarà il seguente modello [ActionModel06]:


using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
  public class ActionModel06
  {
    [Required(ErrorMessage = "Le paramètre [personneId] est requis")]
    public int PersonneId { get; set; }
 
    [Required(ErrorMessage = "Le paramètre [valider] est requis")]
    public string Valider { get; set; }
  }
}

L'azione [Action06] riceve questo modello e lo passa così com'è alla seguente vista [Action06Post] (riga 5 dell'azione):


@model Exemple_03.Models.ActionModel06
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action06Post</title>
</head>
<body>
  <h3>Action06 - POST</h3>
  Valeurs postées :
  <ul>
    <li>ID de la personne sélectionnée : @Model.PersonneId</li>
    <li>Commande utilisée : @Model.Valider</li>
  </ul>
</body>
</html>

Il modello viene visualizzato alle righe 18 e 19.

Vediamo un esempio:

In [1], selezioniamo la terza persona con un [Id] pari a 3. In [2], inviamo il modulo. In [3], i valori ricevuti. In [4,5], vediamo che è stato richiamato lo stesso URL, uno tramite GET [4], l'altro tramite POST [5]. Questo non è visibile nell'URL.

Nella vista visualizzata a seguito del POST, potremmo voler visualizzare il nome e il cognome della persona selezionata anziché il suo numero. Dobbiamo quindi aggiornare la vista POST e il suo modello.

Creiamo un'azione [Action07] per gestire questo caso. Questa azione dovrà utilizzare la sessione dell'utente per memorizzare l'elenco delle persone. Seguiremo lo schema illustrato nella Sezione 4.10, che ci consente di includere nel modello dell'azione i dati provenienti dagli ambiti [Application] e [Session].

Il modello di sessione sarà la seguente classe [SessionModel]:


namespace Exemple_03.Models
{
  public class SessionModel
  {
    public Personne2[] Personnes { get; set; }
  }
}
  • Riga 2: La sessione memorizzerà l'elenco delle persone visualizzate nell'elenco a discesa;

Dobbiamo associare il tipo precedente [SessionModel] a un binder che chiameremo [SessionModelBinder]. Sarà lo stesso descritto a pagina 74:

Image


using System.Web.Mvc;
 
namespace Exemple_03.Infrastructure
{
  public class SessionModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      // render scope data [Session]
      return controllerContext.HttpContext.Session["data"];
    }
  }
}

Il binding tra il modello [SessionModel] e il suo [SessionModelBinder] è definito in [Global.asax]:


public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
      ...
 
      // model binders
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
    }
    // Session
    public void Session_Start()
    {
      Session["data"] = new SessionModel();
    }
  }
  • Riga 8: il modello viene associato al proprio binder in [Application_Start];
  • riga 13: un'istanza di tipo [SessionModel] viene aggiunta alla sessione associata alla chiave [data].

Una volta fatto ciò, l'azione [Action07] è la seguente:


    // Action07-GET
    [HttpGet]
    public ViewResult Action07(SessionModel session)
    {
      ViewModel05 modèleVue = new ViewModel05();
      session.Personnes= modèleVue.Personnes;
      return View("Action07Get", modèleVue);
}
  • riga 3: l'azione recupera un tipo [SessionModel], ovvero i dati dell'ambito [Session] associati alla chiave [data];
  • riga 5: creiamo il modello di vista;
  • riga 6: l'array di persone viene inserito nella sessione. Ne avremo bisogno nella richiesta successiva, la richiesta POST. Il protocollo HTTP è un protocollo senza stato. È necessario utilizzare una sessione per mantenere lo stato tra le richieste. Una sessione è specifica per un utente ed è gestita dal server web;
  • riga 7: viene visualizzata la vista [Action07Get.cshtml]. È la seguente:

@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
...
<body>
  <h3>Action07 - GET</h3>
  <p>Choisissez une personne</p>
  <form method="post" action="/First/Action07">
....
  </form>
</body>
</html>

È identico alla vista [Action06Get.cshtml] che abbiamo già esaminato. La differenza principale si trova alla riga 7: l'URL a cui verranno inviati i valori del modulo. Questi saranno elaborati dalla seguente azione [Action07]:


    // Action07-POST
    [HttpPost]
    public ViewResult Action07(SessionModel session, ActionModel06 modèle)
    {
      Personne2 personne = session.Personnes.Where(p => p.Id == modèle.PersonneId).First<Personne2>();
      string strPersonne = string.Format("{0} {1}", personne.Prénom, personne.Nom);
      return View("Action07Post", (object)strPersonne);
}
  • Riga 3: I valori inviati sono racchiusi nel modello di azione [ActionModel06] già utilizzato in precedenza (di seguito):

using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
  public class ActionModel06
  {
    [Required(ErrorMessage = "Le paramètre [personneId] est requis")]
    public int PersonneId { get; set; }
 
    [Required(ErrorMessage = "Le paramètre [valider] est requis")]
    public string Valider { get; set; }
  }
}

  • riga 3: il primo parametro è il dato nell'ambito [Session] associato alla chiave [data];
  • riga 5: una query LINQ recupera la persona con l'[Id] che è stato inviato;
  • riga 6: costruiamo la stringa da visualizzare dalla vista [Action07Post] (riga 8);
  • riga 7: per chiamare il costruttore [View] corretto, il tipo [string] deve essere convertito in [object].

La vista [Action07Post.cshtml] è la seguente:


@model string
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action07-Post</title>
</head>
<body>
  <h3>Action07-POST</h3>
  Vous avez sélectionné [@Model].
</body>
</html>

  • riga 1: il modello è di tipo [string];
  • riga 16: la stringa viene visualizzata.

Ecco un esempio di esecuzione:

Image

Image

5.6. Modulo – un esempio completo

Nella sezione 2.5.2.1 abbiamo esaminato il seguente modulo HTML:

Image

Esamineremo un'azione [Action08Get] che visualizza (GET) questo modulo e un'azione [Action08Post] che elabora (POST) i valori inseriti dall'utente. Una configurazione classica.

Il modello di vista [1] sopra riportato sarà un'istanza della classe [ViewModel08]. Questa classe fungerà sia da:

  • il modello di visualizzazione prodotto da una richiesta GET sull'azione [Action08Get];
  • il modello per l'azione [Action08Post] per una richiesta POST.

5.6.1. Il modello nell'ambito [Application]

Supponiamo che gli elementi visualizzati dai pulsanti di opzione, dalle caselle di controllo e dai vari elenchi siano dati nell'ambito [Application]. Uno scenario comune. Queste informazioni provengono da un file di configurazione o da un database a cui si accede all'avvio dell'applicazione nel metodo [Application_Start] di [Global.asax]. Questo metodo si evolve come segue:


    protected void Application_Start()
    {
....
 
      // model binders
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
      ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());
 
      // scope data [Application]
      Application["data"] = new ApplicationModel();
}

  • riga 7: il tipo [ApplicationModel], che descriveremo tra poco, è associato al data binder [ApplicationModelBinder] che abbiamo già presentato a pagina 74;
  • riga 10: un'istanza di tipo [ApplicationModel] viene memorizzata nel dizionario dell'applicazione, associata alla chiave [data].

La classe [ApplicationModel] viene utilizzata per incapsulare tutti i dati all'interno dell'ambito [Application]. In questo caso, incapsulerà i dati che il modulo deve visualizzare:


namespace Exemple_03.Models
{
  public class ApplicationModel
  {
    // the collections to be displayed in the form
    public Item[] RadioButtonFieldItems { get; set; }
    public Item[] CheckBoxesFieldItems { get; set; }
    public Item[] DropDownListFieldItems { get; set; }
    public Item[] SimpleChoiceListFieldItems { get; set; }
    public Item[] MultipleChoiceListFieldItems { get; set; }
 
    // initializing fields and collections
    public ApplicationModel()
    {
      RadioButtonFieldItems = new Item[]{
        new Item {Value="1",Label="oui"},
        new Item {Value="2", Label="non"}
      };
      CheckBoxesFieldItems = new Item[]{
        new Item {Value="1",Label="1"},
        new Item {Value="2", Label="2"},
        new Item {Value="3", Label="3"}
      };
      DropDownListFieldItems = new Item[]{
        new Item {Value="1",Label="choix1"},
        new Item {Value="2", Label="choix2"},
        new Item {Value="3", Label="choix3"}
      };
      SimpleChoiceListFieldItems = new Item[]{
        new Item {Value="1",Label="liste1"},
        new Item {Value="2", Label="liste2"},
        new Item {Value="3", Label="liste3"},
        new Item {Value="4", Label="liste4"},
        new Item {Value="5", Label="liste5"}
      };
      MultipleChoiceListFieldItems = new Item[]{
        new Item {Value="1",Label="liste1"},
        new Item {Value="2", Label="liste2"},
        new Item {Value="3", Label="liste3"},
        new Item {Value="4", Label="liste4"},
        new Item {Value="5", Label="liste5"}
      };
    }
    // the collections item
    public class Item
    {
      public string Label { get; set; }
      public string Value { get; set; }
    }
 
  }
}

  • righe 45–49: il controllo del modulo che rappresenta le varie raccolte. [Label] è il testo visualizzato dal controllo del modulo, [Value] è il valore inviato da questo controllo quando viene selezionato;
  • riga 6: la raccolta visualizzata dal pulsante di opzione;
  • riga 7: la raccolta visualizzata dalle caselle di controllo;
  • riga 8: la raccolta visualizzata dall'elenco a discesa;
  • riga 9: la raccolta visualizzata dall'elenco a selezione singola;
  • riga 10: la collezione visualizzata dall'elenco a selezione multipla;
  • righe 13–43: queste collezioni vengono inizializzate dal costruttore senza parametri della classe.

Le varie collezioni popoleranno il seguente modulo:

5.6.2. Il modello per l'azione [Action08Get]

Il modulo precedente verrà visualizzato dall'azione [Action08Get] seguente:


    // Action08-GET
    [HttpGet]
    public ViewResult Action08Get(ApplicationModel application)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return View("Formulaire", new ViewModel08(application));
}
  • Riga 2: [Action08Get] risponderà solo a una richiesta [GET];
  • riga 3: riceve come parametro il modello dell'applicazione appena descritto;
  • riga 5: inizializza i dati nel contenitore dinamico [ViewBag];
  • riga 6: rende la vista [/First/Formulaire.cshtml] utilizzando il modello [ViewModel08]. Questo modello è quello del modulo presentato in precedenza. Per farlo, passiamo il modello dell'applicazione — che definisce gli elementi da visualizzare — al costruttore.

5.6.3. Il modello di vista [Form]

La classe [ViewModel08] sarà il modello del modulo. Questa classe è la seguente:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
 
namespace Exemple_03.Models
{
  public class ViewModel08
  {
    // input fields
    public string RadioButtonField { get; set; }
    public string[] CheckBoxesField { get; set; }
    public string TextField { get; set; }
    public string PasswordField { get; set; }
    public string TextAreaField { get; set; }
    public string DropDownListField { get; set; }
    public string SimpleChoiceListField { get; set; }
    public string[] MultipleChoiceListField { get; set; }
 
    // the collections to be displayed in the form
    public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
    public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
    public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
    public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
    public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
 
    // manufacturers
    public ViewModel08()
    {
    }
 
    public ViewModel08(ApplicationModel application)
    {
      // initialization collections
      RadioButtonFieldItems = application.RadioButtonFieldItems;
      CheckBoxesFieldItems = application.CheckBoxesFieldItems;
      DropDownListFieldItems = application.DropDownListFieldItems;
      SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
      MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
      // field initialization
      RadioButtonField = "2";
      CheckBoxesField = new string[] { "2" };
      TextField = "quelques mots";
      PasswordField = "secret";
      TextAreaField = "ligne1\nligne2";
      DropDownListField = "2";
      SimpleChoiceListField = "3";
      MultipleChoiceListField = new string[] { "1", "3" };
    }
  }
}

  • In un modulo, ci sono due tipi di elementi: quelli che vengono visualizzati e quelli in cui si inseriscono i dati;
  • Le righe 20–24 definiscono gli elementi da visualizzare. Si tratta delle varie raccolte del modulo. Si trovano nel modello dell'applicazione (righe 34–38);
  • Righe 10–17: definiscono i campi di immissione del modulo;
  • riga 10: [RadioButtonField] recupererà il valore inviato dalle righe successive del modulo:


        <!-- radio buttons -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
<input type="radio" name="RadioButtonField" value="1" />oui              
<input type="radio" name="RadioButtonField" value="2" checked=&quot;checked&quot;/>non              
          </td>
</tr>

Si noti nelle righe 5 e 6 che l'attributo [name] dei due pulsanti di opzione è il nome della proprietà che verrà inizializzata. Nei dati inviati, si troverà una stringa nella forma:


param1=val1&RadioButtonField=2&param2=val2

se l'utente ha selezionato l'opzione contrassegnata con [no]. In realtà, è l'attributo [value] dell'opzione selezionata che viene inviato.

  • Riga 11: [CheckBoxesField] recupererà i valori inviati dalle seguenti righe del modulo:


        <!-- checkboxes -->
        <tr>
          <td>Cases à cocher</td>
          <td>
<input type="checkbox" name="CheckBoxesField" value="1" />1              
<input type="checkbox" name="CheckBoxesField" value="2" checked=&quot;checked&quot;/>2              
<input type="checkbox" name="CheckBoxesField" value="3" />3              
</td>

Si noti nelle righe 5 e 6 che l'attributo [name] delle caselle di controllo è il nome della proprietà che verrà inizializzata. Nei dati inviati, si troverà una stringa nel seguente formato:


param1=val1&CheckBoxesField=2&CheckBoxesField=3&param2=val2

se l'utente ha selezionato le caselle di controllo contrassegnate con [2] e [3]. È l'attributo [value] delle opzioni selezionate che viene inviato. Poiché è possibile inviare più parametri con lo stesso nome, [CheckBoxesField] è un array di valori piuttosto che un singolo valore. Se non viene selezionata alcuna casella di controllo, il parametro [CheckBoxesField] sarà assente dalla stringa inviata e la proprietà del modello con lo stesso nome non verrà inizializzata. Questo può essere problematico, come vedremo.

  • Riga 12: [TextField] recupererà il valore inviato dalle seguenti righe del modulo:


          <!-- single-line text input field -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              <input type="text" name="TextField" value="quelques mots" size="30" />
            </td>
</tr>

Riga 5: L'attributo [name] del campo di immissione è il nome della proprietà che verrà inizializzata. Nei dati inviati, troverai una stringa nel seguente formato:


param1=val1&TextField=abcdef&param2=val2

se l'utente ha inserito [abcdef] nel campo di immissione.

  • Riga 13: [PasswordField] recupererà il valore inviato dalle seguenti righe del modulo:


        <!-- password entry field -->
        <tr>
          <td>Mot de passe</td>
          <td>
            <input type="password" name="PasswordField" value="secret" size="30" />
          </td>
</tr>

Riga 5: L'attributo [name] del campo di immissione è il nome della proprietà che verrà inizializzata. Nei dati inviati, troverai una stringa nel seguente formato:


param1=val1&PasswordField=abcdef&param2=val2

se l'utente ha inserito [abcdef] nel campo di immissione.

  • Riga 14: [TextAreaField] recupererà il valore inviato dalle seguenti righe del modulo:


        <!-- le champ de saisie texte multilignes -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            <textarea name="TextAreaField" cols="40" rows="3">ligne1
ligne2</textarea>
          </td>
</tr>

Riga 5: L'attributo [name] del campo di immissione è il nome della proprietà che verrà inizializzata. Nei dati inviati, troverai una stringa nel seguente formato:


param1=val1&TextAreaField=abcdef%0D%OAhijk&param2=val2

se l'utente ha inserito [abcdef] seguito da un'interruzione di riga e [ijk] nel campo di immissione.

  • Riga 15: [DropDownListField] recupererà il valore inviato dalle righe successive del modulo:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>
            <select name="DropDownListField">
<option value="1" >choix1</option>
<option value="2" selected=&quot;selected&quot;>choix2</option>
<option value="3" >choix3</option>
            </select>
</tr>

Riga 5: L'attributo [name] del tag <select> è il nome della proprietà che verrà inizializzata. Nei dati inviati, troverai una stringa nel seguente formato:


param1=val1&DropDownListField=1&param2=val2

se l'utente ha selezionato l'opzione [choice1]. È l'attributo [value] dell'opzione selezionata che viene inviato.

  • Riga 16: [SingleChoiceListField] recupererà il valore inviato dalle seguenti righe del modulo:


        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
            <select name="SimpleChoiceListField" size="3">
<option value="1" >liste1</option>
<option value="2" >liste2</option>
<option value="3" selected=&quot;selected&quot;>liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
 
            </select>
</tr>

Riga 5: L'attributo [name] del tag <select> è il nome della proprietà che verrà inizializzata. L'attributo [size="3"] garantisce che non si abbia un elenco a discesa. Nei dati inviati, troveremo una stringa nel seguente formato:


param1=val1&SimpleChoiceListField=3&param2=val2

se l'utente ha selezionato l'opzione [liste3]. È l'attributo [value] dell'opzione selezionata che viene inviato. Il parametro [SingleChoiceListField] potrebbe essere assente dalla stringa inviata se non è stato selezionato alcun elemento.

  • Riga 17: [MultipleChoiceListField] recupererà i valori inviati dalle seguenti righe del modulo:


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
            <select name="MultipleChoiceListField" size="3" multiple="multiple">
<option value="1" selected=&quot;selected&quot;>liste1</option>
<option value="2" >liste2</option>
<option value="3" selected=&quot;selected&quot;>liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
            </select>
</tr>

Riga 5: L'attributo [name] del tag <select> è il nome della proprietà che verrà inizializzata. L'attributo [size="3"] garantisce che non vi sia alcun elenco a discesa, mentre l'attributo [multiple] consente all'utente di selezionare più elementi tenendo premuto il tasto [Ctrl]. Nei dati inviati, troverai una stringa nel seguente formato:


param1=val1&MultipleChoiceListField=1&MultipleChoiceListField=3&param2=val2

se l'utente ha selezionato le opzioni [list1] e [list3]. È l'attributo [value] delle opzioni selezionate che viene inviato. Poiché è possibile inviare più parametri con lo stesso nome, [MultipleChoiceListField] è un array di valori piuttosto che un singolo valore. Se non è selezionata alcuna casella di controllo, il parametro [MultipleChoiceListField] sarà assente dalla stringa inviata e la proprietà del modello con lo stesso nome non verrà inizializzata.

I vari campi di immissione presentati in precedenza riceveranno i valori inviati dal modulo. Possono anche essere inizializzati prima dell'invio del modulo. Questo è ciò che è stato fatto qui:


      // initialisation champs
      RadioButtonField = "2";
      CheckBoxesField = new string[] { "2" };
      TextField = "quelques mots";
      PasswordField = "secret";
      TextAreaField = "ligne1\nligne2";
      DropDownListField = "2";
      SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };

Se questi valori fossero stati ottenuti dopo una richiesta POST dal modulo, ciò significherebbe che l'utente ha:

  • riga 2: selezionato l'opzione [no] dai pulsanti di opzione;
  • riga 3: selezionato l'opzione [2] nelle caselle di controllo;
  • riga 4: digitato [a few words] nel campo di immissione;
  • riga 5: inserito [secret] come password;
  • riga 6: digitato [riga1\nriga2] nel campo di immissione multilinea;
  • riga 7: selezionata l'opzione [option2] dall'elenco a discesa;
  • riga 8: selezionata l'opzione [list3] dall'elenco a scelta singola;
  • riga 9: selezionate le opzioni [list1] e [list3] dall'elenco a selezione multipla;

Procederemo come se fosse stata effettuata una richiesta POST e volessimo restituire il modulo esattamente come è stato inserito. Questo è esattamente ciò che accade quando viene restituito all'utente un modulo errato. Il modulo viene restituito esattamente come è stato inserito.

5.6.4. La vista [Form]

La vista [/First/Formulaire.cshtml] visualizza il modulo:


@model Exemple_03.Models.ViewModel08
@using Exemple_03.Models
@{
  Layout = null;
}
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Formulaire</title>
</head>
<body>
  <form method="post" action="Action08Post">
    <h2>Formulaire ASP.NET MVC</h2>
    <h3>Affiché par : @ViewBag.info</h3>
    <table>
      <thead></thead>
      <tbody>
        <!-- radio buttons -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
            @foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
            {
              string strChecked = item.Value == @Model.RadioButtonField ? "checked=\"checked\"" : "";
              <input type="radio" name="RadioButtonField" value="@item.Value" @strChecked/>@item.Label
              <text/>
            }
          </td>
        </tr>
...
      </tbody>
    </table>
    <input type="submit" value="Valider" />
  </form>
</body>
</html>
  • riga 1: [ViewModel08] è il modello del modulo;
  • riga 12: il tag <form> per il modulo. Questo modulo verrà inviato utilizzando il metodo [POST] (attributo method) all'URL [/First/Action08Post] (attributo action);
  • riga 33: il pulsante [submit] utilizzato per inviare il modulo;
  • righe 22–27: visualizzano i pulsanti di opzione:

Image

  • riga 22: si esegue un'iterazione attraverso la raccolta visualizzata dal pulsante di opzione;
  • riga 24: il pulsante il cui attributo [value] corrisponde al valore della proprietà [RadioButtonField] deve essere selezionato. Per farlo, deve avere l'attributo [checked="checked"];
  • riga 25: genera il tag <input type="radio"> con il valore [@item.Value] e l'etichetta [@item.Label];
  • riga 26: il tag <text/> non è un tag HTML riconosciuto. È presente per [Razor]. Quando lo incontra, [Razor] genererà un'interruzione di riga. Ciò non ha alcun effetto sul modulo visualizzato, ma influisce sul codice HTML generato. I tag <input type="radio"> si trovano quindi su due righe diverse invece che sulla stessa riga. Ciò rende il codice più leggibile quando si visualizza il codice sorgente della pagina visualizzata nel browser;

Esaminiamo gli altri elementi della vista:


        <!-- checkboxes -->
        <tr>
          <td>Cases à cocher</td>
          <td>
            @{
              foreach (ApplicationModel.Item item in @Model.CheckBoxesFieldItems)
              {
                string strChecked = @Model.CheckBoxesField.Contains(item.Value) ? "checked=\"checked\"" : "";
              <input type="checkbox" name="CheckBoxesField" value="@item.Value" @strChecked/>@item.Label
              <text/>
              }
            }
</td>
  • riga 6: si esegue un'iterazione sulla raccolta visualizzata dalle caselle di controllo;
  • riga 8: una casella di controllo il cui attributo [value] è uno dei valori della proprietà [CheckBoxesField] deve essere selezionata. Per farlo, deve avere l'attributo [checked="checked"]. Usiamo un'espressione LINQ per determinare se un valore è contenuto in un array;
  • riga 25: genera il tag <input type="checkbox"> con il valore [@item.Value] e l'etichetta [@item.Label];

<!-- single-line text input field -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              <input type="text" name="TextField" value="@Model.TextField" size="30" />
            </td>
          </tr>
        <!-- password entry field -->
        <tr>
          <td>Mot de passe</td>
          <td>
            <input type="password" name="PasswordField" value="@Model.PasswordField" size="30" />
          </td>
        </tr>
        <!-- multiline text input field -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            <textarea name="TextAreaField" cols="40" rows="3">@Model.TextAreaField</textarea>
          </td>
        </tr>
  • righe 5, 12: assegniamo il valore del modello all'attributo [value] del tag;
  • riga 19: come sopra, ma con una sintassi diversa.

        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>
            <select name="DropDownListField">
              @{
                foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
                {
                  string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>
  • riga 7: si esegue un'iterazione attraverso la raccolta visualizzata dall'elenco a discesa;
  • riga 9: deve quindi essere selezionata un'opzione il cui attributo [value] corrisponda al valore della proprietà [DropDownListField]. A tal fine, deve avere l'attributo [selected="selected"];
  • riga 25: genera il tag <option value="value">label</option> con il valore [@item.Value] e l'etichetta [@item.Label];

        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
            <select name="SimpleChoiceListField" size="3">
              @{
                foreach (ApplicationModel.Item item in @Model.SimpleChoiceListFieldItems)
                {
                  string strChecked = item.Value == @Model.SimpleChoiceListField ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>

La spiegazione è la stessa di quella relativa all'elenco a discesa.


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
            <select name="MultipleChoiceListField" size="3" multiple="multiple">
              @{
                foreach (ApplicationModel.Item item in @Model.MultipleChoiceListFieldItems)
                {
                  string strChecked = @Model.MultipleChoiceListField.Contains(item.Value) ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>
  • riga 7: si esegue un'iterazione sulla raccolta visualizzata dall'elenco;
  • riga 9: deve essere selezionata un'opzione il cui attributo [value] sia uno dei valori della proprietà [MultipleChoiceListField]. A tal fine, deve avere l'attributo [selected="selected"]. Utilizziamo un'espressione LINQ per determinare se un valore è contenuto in un array;
  • riga 10: generazione del tag <option value="value">label</option> con [@item.Value] come valore e [@item.Label] come etichetta;

5.6.5. Elaborazione del POST del modulo

Abbiamo visto che il modulo verrebbe inviato all'azione [Action08Post]:


  <form method="post" action="Action08Post">

L'azione [Action08Post] è la seguente:


    // Action08-POST
    [HttpPost]
    public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      ViewModel08 modèle = new ViewModel08(application);
      TryUpdateModel(modèle,posted);
      return View("Formulaire", modèle);
}
  • Riga 3: Il modello dell'applicazione viene passato come parametro, insieme ai valori inviati. Questi sono disponibili in un tipo [FormCollection]. Il valore del parametro [RadioButtonField] inviato viene ottenuto utilizzando l'espressione posted["RadioButtonField"]. Questa restituisce una stringa o il puntatore null. Se si scrive posted["CheckBoxesField"], si otterrà un array di stringhe o il puntatore null;
  • quindi perché non scrivere:

public ViewResult Action08Post(ApplicationModel application, ViewModel08 posted)

Ci sono due ragioni:

  • la prima è che il framework istanzierà il modello [ViewModel08] utilizzando il costruttore senza parametri, il che comporterà la mancata inizializzazione delle collezioni del modello;
  • la seconda è che vogliamo controllare cosa viene inserito nel modello. Sappiamo che ci sono quattro possibili fonti per il modello: i parametri di una richiesta GET o POST, il percorso utilizzato e quelli provenienti da un file caricato. In questo caso, vogliamo inizializzare il modello solo con i valori inviati.
  • Riga 6: istanziamo il modello utilizzando il costruttore corretto;
  • riga 7: lo inizializziamo con i valori inviati. Dopo questa operazione, il modello corrisponde all'input dell'utente;
  • Riga 8: visualizziamo nuovamente il modulo. L'utente lo vedrà esattamente come è stato compilato.

Vediamo un esempio:

In [2], il risultato del [POST] riflette accuratamente ciò che è stato inserito in [1].

5.6.6. Gestione degli errori POST

Abbiamo accennato al fatto che se nessun valore è stato selezionato o spuntato per i campi [CheckBoxesField, SimpleChoiceListField, MultipleChoiceListField], i parametri corrispondenti non facevano parte della stringa inviata e, di conseguenza, le proprietà del modello con gli stessi nomi non sono state inizializzate.

Diamo un'occhiata al seguente esempio:

  • in [1], nessuna casella di controllo era selezionata;
  • in [2], il [POST] restituisce una casella di controllo selezionata.

La spiegazione è la seguente:

  • poiché nessuna casella di controllo è selezionata, il parametro [CheckBoxesField] non è incluso nei valori inviati;
  • l'azione [Action08Post] procede come segue:

    [HttpPost]
    public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = ...
      ViewModel08 modèle = new ViewModel08(application);
      TryUpdateModel(modèle,posted);
      return View("Formulaire", modèle);
}
  • Riga 5: Il modello del modulo viene istanziato. Tuttavia, il costruttore utilizzato assegna l'array ["2"] alla proprietà [CheckBoxesField];
  • Riga 6: I valori inviati vengono salvati nel modello. Poiché il parametro [CheckBoxesField] non fa parte dei valori inviati, alla proprietà con lo stesso nome non viene assegnato alcun valore. Mantiene quindi il suo valore ["2"], il che significa che quando il modulo viene visualizzato, la casella di controllo n. 2 risulta selezionata quando invece non dovrebbe esserlo.

Questo problema può essere risolto in vari modi. Abbiamo scelto di risolverlo nel codice dell'azione [Action08Post]:


// Action08-POST
    [HttpPost]
    public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      ViewModel08 modèle = new ViewModel08(application);
      TryUpdateModel(modèle,posted);
      // processing of non-posted values
      if (posted["CheckBoxesField"] == null)
      {
        modèle.CheckBoxesField = new string[] { };
      }
      if (posted["SimpleChoiceListField"] == null)
      {
        modèle.SimpleChoiceListField = "";
      }
      if (posted["MultipleChoiceListField"] == null)
      {
        modèle.MultipleChoiceListField = new string[] { };
      }
      // form display
      return View("Formulaire", modèle);
    }
  • Righe 9–20: Verifichiamo se determinati parametri sono stati inviati o meno. In caso contrario, li inizializziamo con il valore corrispondente al fatto che l'utente non abbia inserito nulla. Questa verifica non è stata eseguita per l'elenco a discesa, che ha sempre un elemento selezionato, a differenza degli altri elenchi.

I lettori sono invitati a testare questa nuova versione.

5.7. Utilizzo di metodi specializzati nella generazione di moduli

5.7.1. Il nuovo modulo

Creiamo un nuovo modulo [Form2.cshtml] che genererà un modulo identico al precedente:

Rivediamo il codice utilizzato per generare l'elenco a discesa del modulo:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>
            <select name="DropDownListField">
              @{
                foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
                {
                  string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>

Questo codice presenta due svantaggi:

  • il più significativo è che la natura del componente — in questo caso, un elenco a discesa — va persa a causa della complessità del codice;
  • Riga 5: se si commette un errore nel nome della proprietà del modello da utilizzare come attributo [name], non se ne accorgerà fino al momento dell'esecuzione.

ASP.NET MVC fornisce metodi specializzati chiamati [HTML Helpers] che, come suggerisce il nome, sono progettati per facilitare la generazione di HTML, in particolare per i moduli. Utilizzando queste classi, l' e dell'elenco a discesa mostrata sopra viene scritta come segue:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>@Html.DropDownListFor(m => m.DropDownListField,
           new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
          </td>
</tr>

L'elenco a discesa viene generato dalle righe 4–5. Il codice è notevolmente meno complesso. Il codice HTML generato per l'elenco a discesa è il seguente:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td><select id="DropDownListField" name="DropDownListField"><option value="1">choix1</option>
<option selected="selected" value="2">choix2</option>
<option value="3">choix3</option>
</select></td>
</tr>
  • riga 4: l'attributo [name] è corretto;
  • Righe 4–6: le opzioni sono state generate correttamente e l'opzione corretta è stata selezionata.

Torniamo al codice che ha generato queste righe HTML:


@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
  • Il primo parametro è una funzione lambda (così si chiama), dove `m` rappresenta il modello di vista e `m.DropDownListField` è una proprietà di quel modello. Il generatore di codice HTML utilizzerà il nome di questa proprietà per generare gli attributi [id] e [name] dell'elemento [select] che verrà generato. Se viene utilizzata una proprietà inesistente, si verificherà un errore in fase di compilazione anziché in fase di esecuzione. Si tratta di un miglioramento rispetto alla soluzione precedente, in cui gli errori di denominazione venivano rilevati solo in fase di esecuzione;
  • il secondo parametro viene utilizzato per specificare la raccolta di elementi che popoleranno l'elenco a discesa. La classe [SelectList] consente di creare questa raccolta:
    • il suo primo parametro è una qualsiasi raccolta di elementi. Qui abbiamo una raccolta di tipo [Item];
    • il suo secondo parametro è la proprietà degli elementi che fornirà il valore del tag <option>. Qui, è la proprietà [Value] della classe [Item];
    • il suo terzo parametro è la proprietà degli elementi che fornirà l'etichetta per il tag <option>. Qui, è la proprietà [Label] della classe [Item];
  • Per determinare quale opzione debba essere selezionata (l'attributo selected), il framework fa ciò che facciamo noi: confronta il valore dell'opzione con il valore corrente della proprietà [DropDownListField].

Ora diamo un'occhiata agli altri metodi che possiamo utilizzare:

Pulsanti di opzione

Il nuovo codice è il seguente:


        <!-- les boutons radio -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
            @{
    foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
    {
              @Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)@item.Label
              <text/>
    }
            }
          </td>
</tr>

Il codice HTML generato è il seguente:


        <!-- radio buttons -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
<input id="RadioButtonField" name="RadioButtonField" type="radio" value="1" />oui              
<input checked="checked" id="RadioButtonField" name="RadioButtonField" type="radio" value="2" />non              
          </td>
</tr>

Il metodo utilizzato è [Html.RadioButtonFor]:

@Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)
  • il primo parametro è la proprietà del modello che verrà associata al pulsante di opzione (l'attributo [name]);
  • il secondo parametro è il valore da assegnare al pulsante di opzione (l'attributo [value]).

Caselle di controllo

Il codice cambia come segue:


        <!-- les cases à cocher -->
        <tr>
          <td>Cases à cocher</td>
          <td>
            @{
              @Html.CheckBoxFor(m=>m.CheckBoxField1) @Model.CheckBoxesFieldItems[0].Label
              @Html.CheckBoxFor(m=>m.CheckBoxField2) @Model.CheckBoxesFieldItems[1].Label
              @Html.CheckBoxFor(m=>m.CheckBoxField3) @Model.CheckBoxesFieldItems[2].Label
            }
</td>

Il metodo utilizzato per generare le caselle di controllo è [Html.CheckBoxFor]:

Html.CheckBoxFor(m=>m.Propriété)

Il parametro è la proprietà booleana del modello che verrà associata alla casella di controllo. Se [Property=true], la casella di controllo sarà selezionata. Se [Property=false], la casella di controllo non sarà selezionata. In tutti i casi, l'attributo [value] è impostato su true. Il codice HTML generato è il seguente:


<input id="Propriété" name="Propriété" type="checkbox" value="true" />
<input name="Propriété" type="hidden" value="false" />
  • riga 1: la casella di controllo con l'attributo [value="true"];
  • riga 2: un campo nascosto (type=hidden) con lo stesso nome [Property] della casella di controllo, con l'attributo [value="false"]. Perché ci sono due tag [input] con lo stesso nome? Ci sono due casi:
  • la casella di controllo alla riga 1 è selezionata. In questo caso, la stringa del parametro inviata è Property=true&Property=false (righe 1 e 2). Poiché la proprietà [Property] si aspetta un solo valore, si potrebbe pensare che il framework assegni il valore [true] a [Property]. Per ottenere questo risultato, basterebbe semplicemente eseguire un OR logico tra i valori ricevuti;
  • la casella di controllo alla riga 1 non è selezionata. In questo caso, la stringa del parametro inviata è Property=false (solo riga 2) e alla proprietà [Property] viene assegnato il valore [false], il che è corretto (la casella di controllo non era selezionata).

Campo di immissione a riga singola

Il nuovo codice è il seguente:


          <!-- le champ de saisie texte monoligne -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              @Html.TextBoxFor(m => m.TextField, new { size = "30" })
            </td>
</tr>

Il codice HTML generato è il seguente:


          <!-- single-line text input field -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              <input id="TextField" name="TextField" size="30" type="text" value="quelques mots" />
            </td>
</tr>

Il metodo utilizzato è il seguente:


@Html.TextBoxFor(m => m.TextField, new { size = "30" })

  • Il primo parametro specifica la proprietà del modello associata al campo di immissione. Il nome della proprietà verrà utilizzato negli attributi [name] e [id] del tag <input> generato, mentre il suo valore verrà assegnato all'attributo [value];
  • Il secondo parametro è una classe anonima che specifica determinati attributi del tag HTML generato, in questo caso l'attributo [size].

Campo di immissione della password

Il nuovo codice è il seguente:


        <!-- le champ de saisie d'un mot de passe -->
        <tr>
          <td>Mot de passe</td>
          <td>
            @Html.PasswordFor(m => m.PasswordField, new { size = "15" })
          </td>
</tr>

Il codice HTML generato è il seguente:


        <!-- password entry field -->
        <tr>
          <td>Mot de passe</td>
          <td>
            <input id="PasswordField" name="PasswordField" size="15" type="password" />
          </td>
</tr>

Il metodo utilizzato è il seguente:


@Html.PasswordFor(m => m.PasswordField, new { size = "15" })

Il comportamento è simile a quello del metodo [Html.TextBoxFor].

Campo di immissione multilinea

Il nuovo codice è il seguente:


        <!-- le champ de saisie texte multilignes -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            @Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
          </td>
</tr>

Il codice HTML generato è il seguente:


        <!-- le champ de saisie texte multilignes -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            <textarea cols="30" id="TextAreaField" name="TextAreaField" rows="5">
ligne1
ligne2</textarea>
          </td>
</tr>

Il metodo utilizzato è il seguente:


@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })

Il comportamento è simile a quello del metodo [Html.TextBoxFor].

Elenco a selezione singola

Il nuovo codice è il seguente:


        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
          @Html.DropDownListFor(m => m.SimpleChoiceListField, new SelectList(@Model.SimpleChoiceListFieldItems, "Value", "Label"), new { size = "3" })
</tr>

e il codice HTML generato è il seguente:


        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
          <select id="SimpleChoiceListField" name="SimpleChoiceListField" size="3">
<option value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>

Abbiamo già trattato il metodo [Html.DropDownListFor]. L'unica differenza qui è il terzo parametro, che viene utilizzato per specificare un attributo [size] diverso da 1. È questa caratteristica che trasforma un elenco a discesa [size=1] in un semplice elenco.

L'elenco a selezione multipla

Il nuovo codice è il seguente:


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
          @Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
</tr>

e il codice HTML generato è il seguente:


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
          <select id="MultipleChoiceListField" multiple="multiple" name="MultipleChoiceListField" size="5">
<option selected="selected" value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>

Il metodo


@Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })

funziona come il metodo [Html.DropDownListFor], tranne per il fatto che genera un elenco a selezione multipla. Le opzioni selezionate sono quelle il cui valore (attributo value) è presente nell'array [MultipleChoiceListField].

Il tag <form> può essere generato anche utilizzando un metodo:


  @using (Html.BeginForm("Action09Post", "First"))
  {
...
  }

Il codice HTML generato è il seguente:


<form action="/First/Action09Post" method="post">    
    ...
</form>

Il metodo


Html.BeginForm("Action09Post", "First")

accetta il nome di un'azione come primo parametro e il nome di un controller come secondo parametro.

5.7.2. Le azioni e il modello

Il modulo verrà visualizzato dall'azione [Action09Get] seguente:


    // Action09-GET
    [HttpGet]
    public ViewResult Action09Get(ApplicationModel application)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return View("Formulaire2", new ViewModel09(application));
}

La vista visualizzata alla riga 6 è [Form2], associata al seguente modello [ViewModel09]:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
 
namespace Exemple_03.Models
{
  public class ViewModel09
  {
    // input fields
    public string RadioButtonField { get; set; }
    public bool CheckBoxField1 { get; set; }
    public bool CheckBoxField2 { get; set; }
    public bool CheckBoxField3 { get; set; }
    public string TextField { get; set; }
    public string PasswordField { get; set; }
    public string TextAreaField { get; set; }
    public string DropDownListField { get; set; }
    public string SimpleChoiceListField { get; set; }
    public string[] MultipleChoiceListField { get; set; }
 
    // collections to be displayed in the form
    public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
    public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
    public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
    public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
    public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
 
    // manufacturers
    public ViewModel09()
    {
    }
 
    public ViewModel09(ApplicationModel application)
    {
      // initialization collections
      RadioButtonFieldItems = application.RadioButtonFieldItems;
      CheckBoxesFieldItems = application.CheckBoxesFieldItems;
      DropDownListFieldItems = application.DropDownListFieldItems;
      SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
      MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
      // field initialization
      RadioButtonField = "2";
      CheckBoxField2 = true;
      TextField = "quelques mots";
      PasswordField = "secret";
      TextAreaField = "ligne1\nligne2";
      DropDownListField = "2";
      SimpleChoiceListField = "3";
      MultipleChoiceListField = new string[] { "1", "3" };
    }
  }
}

[ViewModel09] differisce da [ViewModel08] nella gestione delle caselle di controllo. Anziché utilizzare un array di tre caselle di controllo, sono state utilizzate tre caselle di controllo separate (righe 11–13).

Il modulo verrà elaborato dalla seguente azione [Action09Post]:


    // Action09-POST
    [HttpPost]
    public ViewResult Action09Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      ViewModel09 modèle = new ViewModel09(application);
      TryUpdateModel(modèle, posted);
      // processing of non-posted values
      if (posted["SimpleChoiceListField"] == null)
      {
        modèle.SimpleChoiceListField = "";
      }
      if (posted["MultipleChoiceListField"] == null)
      {
        modèle.MultipleChoiceListField = new string[] { };
      }
      // form display
      return View("Formulaire2", modèle);
}

L'azione [Action09Post] è identica all'azione [Action08Post] tranne che per due punti:

  • riga 18: viene utilizzata la vista [Form2] anziché la vista [Form];
  • non viene più gestita la gestione delle caselle di controllo non selezionate. Questa operazione viene ora gestita correttamente dal metodo [Html.CheckBoxFor].

5.8. Generazione di un modulo dai metadati del modello

Oltre a quelli menzionati sopra, esistono altri metodi per generare un modulo. Uno di essi prevede l'associazione di informazioni a una proprietà del modello che indica al framework MVC quale tag di input generare. Queste informazioni sono chiamate metadati.

Si consideri il seguente modello di vista [ViewModel10]:


using System;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
 
namespace Exemple_03.Models
{
  public class ViewModel10
  {
    [Display(Name="Text")]
    [DataType(DataType.Text)]
    public string Text { get; set; }
 
    [Display(Name = "TextArea")]
    [DataType(DataType.MultilineText)]
    public string MultiLineText { get; set; }
 
    [Display(Name = "Number")]
    public int Number { get; set; }
 
    [Display(Name = "Decimal")]
    [UIHint("Decimal")]
    public double Decimal { get; set; }
 
    [Display(Name = "Tel")]
    [DataType(DataType.PhoneNumber)]
    public string Tel { get; set; }
 
    [Display(Name = "Date")]
    [DataType(DataType.Date)]
    public DateTime Date { get; set; }
 
    [Display(Name = "Time")]
    [DataType(DataType.Time)]
    public DateTime Time { get; set; }
 
    [Display(Name = "HiddenInput")]
    [UIHint("HiddenInput")]
    public string HiddenInput { get; set; }
 
    [Display(Name = "Boolean")]
    [UIHint("Boolean")]
    public bool Boolean { get; set; }
 
    [Display(Name = "Email")]
    [DataType(DataType.EmailAddress)]
    public string Email{ get; set; }
 
    [Display(Name = "Url")]
    [DataType(DataType.Url)]
    public string Url { get; set; }
 
    [Display(Name = "Password")]
    [DataType(DataType.Password)]
    public string Password { get; set; }
 
    [Display(Name = "Currency")]
    [DataType(DataType.Currency)]
    public double Currency { get; set; }
 
    [Display(Name = "CreditCard")]
    [DataType(DataType.CreditCard)]
    public string CreditCard { get; set; }
 
    // manufacturer
    public ViewModel10()
    {
      Text = "tra la la";
      MultiLineText = "ligne1\nligne2";
      Number = 4;
      Decimal = 10.2;
      Tel = "0617181920";
      Date = DateTime.Now;
      Time = DateTime.Now;
      HiddenInput = "caché";
      Boolean = true;
      Email = "x@y.z";
      Url = "http://istia.univ-angers.fr";
      Password = "mdp";
      Currency = 4.2;
      CreditCard = "0123456789012345";
    }
  }
}

I metadati sono costituiti dai tag [Display, DataType, UIHint].

Questo modello di vista verrà costruito dalla seguente azione [Action10Get]:


    // Action10-GET
    [HttpGet]
    public ViewResult Action10Get()
    {
      return View(new ViewModel10());
}

Nella riga 5 sopra, indichiamo alla vista predefinita per l'azione [/First/Action10Get.cshtml] di visualizzare il modello di vista di tipo [ViewModel10]. Questa vista è la seguente:


@model Exemple_03.Models.ViewModel10
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action10Get</title>
</head>
<body>
  <h3>Formulaire ASP.NET MVC - 2</h3>
  @using (Html.BeginForm("Action10Post", "First"))
  {
    <table>
      <thead>
        <tr>
          <th>LabelFor</th>
          <th>EditorFor</th>
          <th>DisplayFor</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>@Html.LabelFor(m => m.Text)</td>
          <td>@Html.EditorFor(m => m.Text)</td>
          <td>@Html.DisplayFor(m => m.Text)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.MultiLineText)</td>
          <td>@Html.EditorFor(m => m.MultiLineText)</td>
          <td>@Html.DisplayFor(m => m.MultiLineText)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Number)</td>
          <td>@Html.EditorFor(m => m.Number)</td>
          <td>@Html.DisplayFor(m => m.Number)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Decimal)</td>
          <td>@Html.EditorFor(m => m.Decimal)</td>
          <td>@Html.DisplayFor(m => m.Decimal)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Tel)</td>
          <td>@Html.EditorFor(m => m.Tel)</td>
          <td>@Html.DisplayFor(m => m.Tel)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Date)</td>
          <td>@Html.EditorFor(m => m.Date)</td>
          <td>@Html.DisplayFor(m => m.Date)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Time)</td>
          <td>@Html.EditorFor(m => m.Time)</td>
          <td>@Html.DisplayFor(m => m.Time)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.HiddenInput)</td>
          <td>@Html.EditorFor(m => m.HiddenInput)</td>
          <td>@Html.DisplayFor(m => m.HiddenInput)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Boolean)</td>
          <td>@Html.EditorFor(m => m.Boolean)</td>
          <td>@Html.DisplayFor(m => m.Boolean)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Email)</td>
          <td>@Html.EditorFor(m => m.Email)</td>
          <td>@Html.DisplayFor(m => m.Email)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Url)</td>
          <td>@Html.EditorFor(m => m.Url)</td>
          <td>@Html.DisplayFor(m => m.Url)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Password)</td>
          <td>@Html.EditorFor(m => m.Password)</td>
          <td>@Html.DisplayFor(m => m.Password)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Currency)</td>
          <td>@Html.EditorFor(m => m.Currency)</td>
          <td>@Html.DisplayFor(m => m.Currency)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.CreditCard)</td>
          <td>@Html.EditorFor(m => m.CreditCard)</td>
          <td>@Html.DisplayFor(m => m.CreditCard)</td>
        </tr>
      </tbody>
    </table>
    <input type="submit" value="Valider" />
  }
</body>
</html>

Per ogni proprietà del modello, utilizziamo il metodo:

  • Html.LabelFor per visualizzare il valore dei metadati [DisplayName] della proprietà;
  • Html.EditorFor per generare il tag HTML di input per il valore della proprietà. Questo metodo utilizza i metadati [DataType] e [UIHint] della proprietà;
  • Html.DisplayFor per visualizzare il valore della proprietà nel formato specificato dai metadati [DataType].

Ecco un esempio di come funziona nel browser Chrome:

Image

A seconda del browser utilizzato, le pagine potrebbero apparire in modo diverso. Questo perché la vista generata utilizza i nuovi tag introdotti dalla versione 5 dell'HTML, nota come HTML5. Non tutti i browser supportano ancora questa versione. Nell'esempio sopra riportato, il browser Chrome la supporta parzialmente.

5.8.1. Il [POST] del modulo

Il [POST] del modulo viene gestito dalla seguente azione [Action10Post]:


    // Action10-POST
    [HttpPost]
    public ContentResult Action10Post(ViewModel10 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, valide={2}, erreurs={3}", RouteData.Values["controller"], RouteData.Values["action"], ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • riga 3: l'azione [Action10Post] utilizza il modulo inviato come modello di input;
  • riga 5: recuperiamo gli errori di convalida da questo modulo;
  • riga 6: viene preparata la risposta di testo al client;
  • riga 7: la inviamo.

Esaminiamo ora una per una le proprietà del modello [ViewModel10] e vediamo come i metadati associati influenzano l'HTML generato e la convalida dei campi di input.

5.8.2. Proprietà [Text]

Definizione


    [Display(Name="Text")]
    [DataType(DataType.Text)]
    public string Text { get; set; }
...
Text = "tra la la";

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Text)</td>
        <td>@Html.EditorFor(m => m.Text)</td>
        <td>@Html.DisplayFor(m => m.Text)</td>
</tr>

Visivo

Image

HTML generato


      <tr>
        <td><label for="Text">Text</label></td>
        <td><input class="text-box single-line" id="Text" name="Text" type="text" value="tra la la" /></td>
        <td>tra la la</td>
</tr>

Commenti

  • Il metodo [Html.LabelFor] ha generato il tag <label> alla riga 2. Il valore dell'attributo [for] è il nome della proprietà del parametro del metodo [Html.LabelFor]


public string Text { get; set; }

Il testo visualizzato tra l'inizio e la fine del tag è il testo dei metadati


[Display(Name="Text")]

Il metodo [Html.LabelFor] funziona sempre in questo modo. Non torneremo su questo argomento per le altre proprietà.

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3. Si noti che ha un attributo [class] che associa la classe CSS [text-box single-line] al tag. Gli attributi [id] e [name] hanno il valore [Text], che è il nome della proprietà del parametro del metodo [Html.EditorFor]. L'attributo [type] ha il valore [text] a causa dei metadati


[DataType(DataType.Text)]

  • il metodo [Html.DisplayFor] ha generato il testo alla riga 4. Questo è il valore della proprietà del parametro del metodo [Html.DisplayFor]. Questo metodo è influenzato dai metadati


[DataType(DataType.Text)]

, che fa sì che il valore venga visualizzato come testo non formattato.

5.8.3. Proprietà [MultiLineText]

Definizione


    [Display(Name = "TextArea")]
    [DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.MultiLineText)</td>
        <td>@Html.EditorFor(m => m.MultiLineText)</td>
        <td>@Html.DisplayFor(m => m.MultiLineText)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="MultiLineText">TextArea</label></td>
        <td><textarea class="text-box multi-line" id="MultiLineText" name="MultiLineText">
ligne1
ligne2</textarea></td>
        <td>ligne1
ligne2</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <textarea> alla riga 3. Si noti che presenta un attributo [class] che associa la classe CSS [text-box multi-line] al tag. Gli attributi [id] e [name] hanno il valore [MultiLineText], che corrisponde al nome della proprietà del parametro del metodo [Html.EditorFor]. È sempre così. Non ne parleremo più. Il tag generato è <textarea> a causa dei metadati


[DataType(DataType.MultilineText)]

che specificava che la proprietà era un testo multilinea.

  • Il metodo [Html.DisplayFor] ha generato il testo per le righe 4–5. Questo è il valore della proprietà del parametro del metodo [Html.DisplayFor].

5.8.4. Proprietà [Number]

Definizione


    [Display(Name = "Number")]
public int Number { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Number)</td>
        <td>@Html.EditorFor(m => m.Number)</td>
        <td>@Html.DisplayFor(m => m.Number)</td>
</tr>

Visual

Image

HTML generato


<tr>
        <td><label for="Number">Number</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-number="Le champ Number doit être un nombre." data-val-required="Le champ Number est requis." id="Number" name="Number" type="number" value="4" /></td>
        <td>4</td>
      </tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] impostato su [number]. Apparentemente, ciò è dovuto semplicemente al fatto che la proprietà è di tipo [int]. Gli attributi [data-val], [data-val-number] e [data-val-required] non sono riconosciuti da HTML5. Sono utilizzati da un framework di convalida dei dati JavaScript lato client;
  • Il metodo [Html.DisplayFor] ha generato il testo alla riga 4, che corrisponde al valore della proprietà.

Convalida

Gli attributi [data-x] influenzano la convalida dei dati lato client. Ecco due esempi:

Inseriamo un numero errato e inviamo:

Image

Nell'esempio sopra riportato, la convalida è avvenuta sul lato client. Il modulo non verrà inviato finché l'errore non sarà stato corretto.

Un altro esempio: non inseriamo nulla:

Nel punto [1] sopra, [Action10Post] segnala un errore. Ricorderete che in precedenza ottenevamo questo comportamento utilizzando l'attributo [Required] sulla proprietà da convalidare (vedi 63), in questo caso la proprietà [Number]. Qui non abbiamo dovuto farlo.

5.8.5. Proprietà [Decimal]

Definizione


    [Display(Name = "Decimal")]
    [UIHint("Decimal")]
public double Decimal { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Decimal)</td>
        <td>@Html.EditorFor(m => m.Decimal)</td>
        <td>@Html.DisplayFor(m => m.Decimal)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Decimal">Decimal</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-number="Le champ Decimal doit être un nombre." data-val-required="Le champ Decimal est requis." id="Decimal" name="Decimal" type="text" value="10,20" /></td>
        <td>10,20</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [text]. Gli altri attributi sono identici a quelli generati per la precedente proprietà [Number]. I metadati:


[UIHint("Decimal")]

fa sì che il valore della proprietà venga visualizzato con due cifre decimali sia per il metodo [Html.EditorFor] che per il metodo [Html.DisplayFor]

Convalida

A differenza del caso precedente, non vengono segnalati errori di convalida sul lato client. L'errore viene segnalato solo dall'azione [Action10Post]. Anche in questo caso, il numero decimale è obbligatorio senza bisogno di impostare l'attributo [Required].

5.8.6. Proprietà [Tel]

Definizione


    [Display(Name = "Tel")]
    [DataType(DataType.PhoneNumber)]
public string Tel { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Tel)</td>
        <td>@Html.EditorFor(m => m.Tel)</td>
        <td>@Html.DisplayFor(m => m.Tel)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Tel">Tel</label></td>
        <td><input class="text-box single-line" id="Tel" name="Tel" type="tel" value="0617181920" /></td>
        <td>0617181920</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [tel]. Questo valore è stato generato in base ai metadati:


[DataType(DataType.PhoneNumber)]

Il tipo [tel] per un tag <input> è una nuova funzionalità di HTML5. Il browser Chrome lo ha trattato come un tag <input> con il tipo [text].

Convalida

Non vengono segnalati errori di convalida né sul lato client né sul lato server. È possibile inserire qualsiasi cosa.

5.8.7. Proprietà [Data]

Definizione


    [Display(Name = "Date")]
    [DataType(DataType.Date)]
public DateTime Date { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Date)</td>
        <td>@Html.EditorFor(m => m.Date)</td>
        <td>@Html.DisplayFor(m => m.Date)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Date">Date</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-date="Le champ Date doit être une date." data-val-required="Le champ Date est requis." id="Date" name="Date" type="date" value="11/10/2013" /></td>
        <td>11/10/2013</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [date]. Questo valore è stato generato in base ai metadati:


[DataType(DataType.Date)]

Il tipo [date] per un tag <input> è una nuova funzionalità di HTML5. Il browser Chrome lo riconosce e consente di inserire la data utilizzando un calendario. Inoltre, la data inserita viene visualizzata nel formato [gg/mm/aaaa], il che significa che Chrome adatta il formato della data alle [impostazioni locali] del browser.

  • Anche il metodo [Html.DisplayFor] ha scritto la data nel formato [gg/mm/aaaa], sempre grazie alla presenza dei metadati [Date].

Convalida

Una data non valida viene segnalata sul lato client [1], impedendo l'invio del modulo al server.

L'assenza di una data non viene segnalata sul lato client, ma viene segnalata sul lato server [2].

5.8.8. Proprietà [Time]

Definizione


    [Display(Name = "Time")]
    [DataType(DataType.Time)]
public DateTime Time { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Time)</td>
        <td>@Html.EditorFor(m => m.Time)</td>
        <td>@Html.DisplayFor(m => m.Time)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Time">Time</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-required="Le champ Time est requis." id="Time" name="Time" type="time" value="11:17" /></td>
        <td>11:17</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [time]. Questo valore è stato generato in base ai metadati:


[DataType(DataType.Time)]

Il tipo [time] per un tag <input> è una nuova funzionalità di HTML5. Il browser Chrome lo riconosce e consente di inserire un'ora nel formato [hh:mm];

  • Anche il metodo [Html.DisplayFor] ha visualizzato l'ora nel formato [hh:mm], sempre a causa della presenza dei metadati [Time].

Convalida

È tecnicamente impossibile inserire un'ora non valida. L'assenza di un'ora viene segnalata sul lato server:

Image

5.8.9. Proprietà [HiddenInput]

Definizione


    [Display(Name = "HiddenInput")]
    [UIHint("HiddenInput")]
public string HiddenInput { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.HiddenInput)</td>
        <td>@Html.EditorFor(m => m.HiddenInput)</td>
        <td>@Html.DisplayFor(m => m.HiddenInput)</td>
</tr>

Visivo

Image

HTML generato


      <tr>
        <td><label for="HiddenInput">HiddenInput</label></td>
        <td>cach&#233;<input id="HiddenInput" name="HiddenInput" type="hidden" value="caché" /></td>
        <td>cach&#233;</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] impostato su [hidden], ovvero un campo nascosto (ma che viene comunque inviato). Questo valore è stato generato a causa dei metadati:


[UIHint("HiddenInput")]

  • Il metodo [Html.DisplayFor] ha scritto il valore del campo nascosto.

5.8.10. Proprietà [Boolean]

Definizione


    [Display(Name = "Boolean")]
public bool Boolean { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Boolean)</td>
        <td>@Html.EditorFor(m => m.Boolean)</td>
        <td>@Html.DisplayFor(m => m.Boolean)</td>
</tr>

Visivo

Image

HTML generato


      <tr>
        <td><label for="Boolean">Boolean</label></td>
        <td><input checked="checked" class="check-box" data-val="true" data-val-required="Le champ Boolean est requis." id="Boolean" name="Boolean" type="checkbox" value="true" /><input name="Boolean" type="hidden" value="false" /></td>
        <td><input checked="checked" class="check-box" disabled="disabled" type="checkbox" /></td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] impostato su [checkbox], ovvero una casella di controllo. Questo valore è stato generato perché la proprietà è di tipo booleano:


public bool Boolean { get; set; }

  • Il metodo [Html.DisplayFor] ha generato la riga 4, anch'essa una casella di controllo (attributo "type") ma disabilitata (attributo "disabled").

5.8.11. Proprietà [Email]

Definizione


    [Display(Name = "Email")]
    [DataType(DataType.EmailAddress)]
public string Email{ get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Email)</td>
        <td>@Html.EditorFor(m => m.Email)</td>
        <td>@Html.DisplayFor(m => m.Email)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Email">Email</label></td>
        <td><input class="text-box single-line" id="Email" name="Email" type="email" value="x@y.z" /></td>
        <td><a href="mailto:x@y.z">x@y.z</a></td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [email]. Questo tipo è una novità dell'HTML5. È stato generato a causa dei metadati:


[DataType(DataType.EmailAddress)]

Chrome sembra aver trattato questo tipo come un tipo [text].

  • Il metodo [Html.DisplayFor] ha generato la riga 4: un link all'indirizzo e-mail.

Convalida

Sul lato client viene segnalato un indirizzo non valido [1]:

Lasciare il campo vuoto non genera un errore.

5.8.12. Proprietà [Url]

Definizione


    [Display(Name = "Url")]
    [DataType(DataType.Url)]
public string Url { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Url)</td>
        <td>@Html.EditorFor(m => m.Url)</td>
        <td>@Html.DisplayFor(m => m.Url)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Url">Url</label></td>
        <td><input class="text-box single-line" id="Url" name="Url" type="url" value="http://istia.univ-angers.fr" /></td>
        <td><a href="http://istia.univ-angers.fr">http://istia.univ-angers.fr</a></td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [url]. Questo tipo è una novità dell'HTML5. È stato generato a causa dei metadati:


[DataType(DataType.Url)]

Chrome sembra trattare questo tipo come un tipo [text].

  • Il metodo [Html.DisplayFor] ha generato la riga 4: un collegamento all'URL.

Convalida

Viene segnalato un URL non valido sul lato client [1]:

L'assenza di input non causa un errore.

5.8.13. Proprietà [Password]

Definizione


    [Display(Name = "Password")]
    [DataType(DataType.Password)]
public string Password { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Password)</td>
        <td>@Html.EditorFor(m => m.Password)</td>
        <td>@Html.DisplayFor(m => m.Password)</td>
</tr>

Visivo

Image

HTML generato


      <tr>
        <td><label for="Password">Password</label></td>
        <td><input class="text-box single-line password" id="Password" name="Password" type="password" value="mdp" /></td>
        <td>mdp</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [password]. Questo tipo è stato generato in base ai metadati:


[DataType(DataType.Password)]

  • Il metodo [Html.DisplayFor] ha generato la riga 4.

5.8.14. Proprietà [Currency]

Definizione


    [Display(Name = "Currency")]
    [DataType(DataType.Currency)]
public double Currency { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.Currency)</td>
        <td>@Html.EditorFor(m => m.Currency)</td>
        <td>@Html.DisplayFor(m => m.Currency)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="Currency">Currency</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-number="Le champ Currency doit être un nombre." data-val-required="Le champ Currency est requis." id="Currency" name="Currency" type="text" value="4,2" /></td>
        <td>4,20 €</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] impostato su [text];

  • Il metodo [Html.DisplayFor] ha generato la riga 4, un numero con due cifre decimali e un simbolo di valuta. Questo formato è stato utilizzato a causa dei metadati:


[DataType(DataType.Currency)]

Convalida

Un valore non valido [1] o un valore mancante [2] viene segnalato sul lato server:

5.8.15. Proprietà [CreditCard]

Definizione


    [Display(Name = "CreditCard")]
    [DataType(DataType.CreditCard)]
public string CreditCard { get; set; }

Visualizza


      <tr>
        <td>@Html.LabelFor(m => m.CreditCard)</td>
        <td>@Html.EditorFor(m => m.CreditCard)</td>
        <td>@Html.DisplayFor(m => m.CreditCard)</td>
</tr>

Visual

Image

HTML generato


      <tr>
        <td><label for="CreditCard">CreditCard</label></td>
        <td><input class="text-box single-line" id="CreditCard" name="CreditCard" type="text" value="0123456789012345" /></td>
        <td>0123456789012345</td>
</tr>

Commenti

  • Il metodo [Html.EditorFor] ha generato il tag <input> alla riga 3 con un attributo [type] di tipo [text]. Il metodo [Html.DisplayFor] ha generato la riga 4. In questo caso non è chiaro quale sia il contributo dei metadati:


[DataType(DataType.CreditCard)]

Convalida

Non viene eseguita alcuna convalida, né sul lato client né sul lato server.

5.9. Convalida del modulo

Abbiamo già affrontato la questione della convalida del modello di un'azione nella Sezione 4.5 e nelle sezioni successive. Riprendiamo questo argomento nel contesto di un modulo:

  • come notificare all'utente gli errori di immissione;
  • eseguire le convalide sia sul lato client che sul lato server per segnalare gli errori all'utente più rapidamente.

5.9.1. Convalida lato server

Consideriamo il seguente modello:


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Mail;
 
namespace Exemple_03.Models
{
  public class ViewModel11 : IValidatableObject
  {
 
    [Required(ErrorMessage = "Information requise")]
    [Display(Name = "Chaîne d'au moins quatre caractères")]
    [RegularExpression(@"^.{4,}$", ErrorMessage = "Information incorrecte")]
    public string Chaine1 { get; set; }
 
    [Display(Name = "Chaîne d'au plus quatre caractères")]
    [Required(ErrorMessage = "Information requise")]
    [RegularExpression(@"^.{1,4}$", ErrorMessage = "Information incorrecte")]
    public string Chaine2 { get; set; }
 
    [Required(ErrorMessage = "Information requise")]
    [Display(Name = "Chaîne de quatre caractères exactement")]
    [RegularExpression(@"^.{4,4}$", ErrorMessage = "Information incorrecte")]
    public string Chaine3 { get; set; }
 
    [Required(ErrorMessage = "Information requise")]
    [Display(Name = "Nombre entier")]
    public int Entier1 { get; set; }
 
    [Display(Name = "Nombre entier dans l'intervalle [1,100]")]
    [Required(ErrorMessage = "Information requise")]
    [Range(1, 100, ErrorMessage = "Information incorrecte")]
    public int Entier2 { get; set; }
 
    [Display(Name = "Nombre réel")]
    [Required(ErrorMessage = "Information requise")]
    public double Reel1 { get; set; }
 
    [Display(Name = "Nombre réel dans l'intervalle [10.2, 11.3]")]
    [Required(ErrorMessage = "Information requise")]
    [Range(10.2, 11.3, ErrorMessage = "Information incorrecte")]
    public double Reel2 { get; set; }
 
    [Display(Name = "Adresse mail")]
    [Required(ErrorMessage = "Information requise")]
    public string Email1 { get; set; }
 
    [Display(Name = "Date sous la forme dd/jj/aaaa")]
    [RegularExpression(@"\s*\d{2}/\d{2}/\d{4}\s*", ErrorMessage = "Information incorrecte")]
    [Required(ErrorMessage = "Information requise")]
    public string Regexp1 { get; set; }
 
    [Display(Name = "Date postérieure à celle d'aujourd'hui")]
    [Required(ErrorMessage = "Information requise")]
    [DataType(DataType.Date)]
    public DateTime Date1 { get; set; }
 
    // validation
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      // Date 1
      if (Date1.Date <= DateTime.Now.Date)
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
      }
      // Email1
      try
      {
        new MailAddress(Email1);
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
      }
      // return the list of errors
      return résultats;
    }
  }
}

Questo modello verrà visualizzato dalla seguente vista [Action11Get.cshtml]:


@model Exemple_03.Models.ViewModel11
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action11Get</title>
  <link rel="stylesheet" href="~/Content/Site.css" />
</head>
<body>
  <h3>Formulaire ASP.NET MVC – Validation 1</h3>
  @using (Html.BeginForm("Action11Post", "First"))
  {
    <table>
      <thead>
        <tr>
          <th>Type attendu</th>
          <th>Valeur saisie</th>
          <th>Message d'erreur</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>@Html.LabelFor(m => m.Chaine1)</td>
          <td>@Html.EditorFor(m => m.Chaine1)</td>
          <td>@Html.ValidationMessageFor(m => m.Chaine1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Chaine2)</td>
          <td>@Html.EditorFor(m => m.Chaine2)</td>
          <td>@Html.ValidationMessageFor(m => m.Chaine2)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Chaine3)</td>
          <td>@Html.EditorFor(m => m.Chaine3)</td>
          <td>@Html.ValidationMessageFor(m => m.Chaine3)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Entier1)</td>
          <td>@Html.EditorFor(m => m.Entier1)</td>
          <td>@Html.ValidationMessageFor(m => m.Entier1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Entier2)</td>
          <td>@Html.EditorFor(m => m.Entier2)</td>
          <td>@Html.ValidationMessageFor(m => m.Entier2)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Reel1)</td>
          <td>@Html.EditorFor(m => m.Reel1)</td>
          <td>@Html.ValidationMessageFor(m => m.Reel1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Reel2)</td>
          <td>@Html.EditorFor(m => m.Reel2)</td>
          <td>@Html.ValidationMessageFor(m => m.Reel2)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Email1)</td>
          <td>@Html.EditorFor(m => m.Email1)</td>
          <td>@Html.ValidationMessageFor(m => m.Email1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Regexp1)</td>
          <td>@Html.EditorFor(m => m.Regexp1)</td>
          <td>@Html.ValidationMessageFor(m => m.Regexp1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Date1)</td>
          <td>@Html.EditorFor(m => m.Date1)</td>
          <td>@Html.ValidationMessageFor(m => m.Date1)</td>
        </tr>
      </tbody>
    </table>
    <p>
      <input type="submit" value="Valider" />
    </p>
  }
</body>
</html>

  • riga 12: fa riferimento al foglio di stile [Site.css]. Per impostazione predefinita, contiene classi utilizzate per evidenziare gli errori di inserimento nei moduli;
  • righe 18–25: una tabella a tre colonne:
    • La colonna 1 visualizza il testo utilizzando il metodo [Html.LabelFor];
    • La colonna 2 visualizza l'input utilizzando il metodo [Html.EditorFor],
    • La colonna 3 visualizza eventuali errori di immissione utilizzando il metodo [Html.ValidationMessageFor];

L'azione [Action11Get] viene utilizzata per visualizzare il modulo:


    // Action11-GET
    [HttpGet]
    public ViewResult Action11Get()
    {
      return View("Action11Get", new ViewModel11());
}

L'azione [Action11Post] viene utilizzata per visualizzare nuovamente il modulo in caso di errori di immissione dati:


    // Action11-POST
    [HttpPost]
    public ViewResult Action11Post(ViewModel11 modèle)
    {
      return View("Action11Get", modèle);
}

  • Riga 3: Il modello [ViewModel11] viene creato e quindi inizializzato con i valori inviati. A questo punto possono verificarsi degli errori. Ogni proprietà non valida P del modello è associata a un messaggio di errore. Questo è il messaggio restituito dal metodo [Html.ValidationMessageFor] del modulo.

Ecco un esempio di esecuzione:

Image

Image

Ecco un altro esempio:

Image

Si noti che entrambe le date sono errate (la data odierna è 10/11/2013) ma gli errori non vengono segnalati. Questi errori vengono rilevati dal metodo [Validate] del modello:


    // validation
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      // Date 1
      if (Date1.Date <= DateTime.Now.Date)
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
      }
      // Email1
      try
      {
        new MailAddress(Email1);
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
      }
      // Regexp1
      try
      {
        DateTime.ParseExact(Regexp1, "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("fr-FR"));
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Regexp1" }));
      }
 
      // on rend la liste des erreurs
      return résultats;
}

Il metodo [Validate] viene eseguito solo dopo che tutte le validazioni degli attributi sono state superate. Ciò è illustrato in un esempio finale:

Image

5.9.2. Convalida lato client

Tutte le convalide precedenti sono state eseguite sul lato server. Ciò richiede uno scambio di dati tra il client e il server affinché l'utente possa visualizzare i propri errori. La convalida lato client utilizza codice JavaScript per notificare all'utente gli errori il prima possibile e, in ogni caso, prima della richiesta POST. La richiesta POST può avvenire solo una volta che tutti gli errori rilevati sono stati corretti.

Useremo il modello [ViewModel11] precedente, ma ora lo visualizzeremo utilizzando la seguente vista [Action12Get.cshtml]:


@model Exemple_03.Models.ViewModel11
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action12Get</title>
  <link rel="stylesheet" href="~/Content/Site.css" />
  <script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js" ></script>
  <script type="text/javascript" src="~/Scripts/jquery.validate.min.js" ></script>
  <script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js" ></script>
</head>
<body>
  <h3>Formulaire ASP.NET MVC - Validation 1</h3>
  @using (Html.BeginForm("Action11Post", "First"))
  {
    <table>
      <thead>
        <tr>
          <th>Type attendu</th>
          <th>Valeur saisie</th>
          <th>Message d'erreur</th>
        </tr>
      </thead>
      <tbody>
...
      </tbody>
    </table>
    <p>
      <input type="submit" value="Valider" />
    </p>
  }
</body>
</html>

Nota: riga 13 — modifica la versione di jQuery in modo che corrisponda a quella della tua versione di Visual Studio (vedi sotto).

La convalida lato client richiede che la riga 3 riportata di seguito sia presente nel file [Web.config] dell'applicazione.


  <appSettings>
    ...
    <add key="ClientValidationEnabled" value="true" />
</appSettings>

  • Righe 1–4: La sezione [appSettings] deve essere un elemento figlio diretto della sezione [configuration] nel file [Web.config];

La vista [Action12Get] è identica alla precedente vista [Action11Get] ad eccezione delle righe 13–15. Queste righe includono gli script JavaScript necessari per la convalida lato client nella vista. Questi script si trovano nella cartella [Scripts] del progetto:

Ogni script ha una versione normale [.js] e una versione minificata [min.js]. La seconda versione è più piccola ma illeggibile. Viene utilizzata in produzione. La versione leggibile viene utilizzata in fase di sviluppo.

La vista [Action12Get.cshtml] verrà visualizzata dall'azione [Action12Get] seguente:


    // Action12-GET
    [HttpGet]
    public ViewResult Action12Get()
    {
      return View("Action12Get", new ViewModel11());
}

Il modulo inviato verrà elaborato dalla seguente azione [Action12Post]:


    // Action12-POST
    [HttpPost]
    public ViewResult Action12Post(ViewModel11 modèle)
    {
      return View("Action12Get", modèle);
}

Vediamo come funziona con un esempio:

Non appena digiti un carattere in [1], appare il messaggio in [2] perché il valore previsto deve essere lungo almeno quattro caratteri. Pertanto, la convalida avviene ad ogni nuovo carattere digitato. Il messaggio di errore scompare dopo aver digitato il quarto carattere. Fatto ciò, inviamo il modulo:

L'URL [3] ci mostra che il [POST] non è stato effettuato. Tuttavia, cliccando sul pulsante [Convalida] sono state attivate tutte le convalide lato client e sono comparsi nuovi messaggi di errore.

Diamo un'occhiata all'HTML generato per la prima voce, ad esempio:


        <tr>
          <td><label for="Chaine1">Cha&#238;ne d&#39;au moins quatre caract&#232;res</label></td>
          <td><input class="text-box single-line" data-val="true" data-val-regex="Information incorrecte" data-val-regex-pattern="^.{4,}$" data-val-required="Information requise" id="Chaine1" name="Chaine1" type="text" value="" /></td>
          <td><span class="field-validation-valid" data-valmsg-for="Chaine1" data-valmsg-replace="true"></span></td>
</tr>

  • La riga 3 contiene:
    • il messaggio di errore quando manca un dato [data-val-required],
    • il messaggio di errore quando l'input è errato [data-val-regex],
    • l'espressione regolare per la stringa inserita [data-val-regex-pattern];
  • riga 4, altri attributi [data-x] utilizzati per visualizzare eventuali messaggi di errore;

Gli attributi [data-x] dei tag generati vengono utilizzati dal codice JavaScript che abbiamo incorporato nella vista. Se questo codice JavaScript manca, tali attributi vengono semplicemente ignorati e non viene eseguita alcuna convalida lato client. Funziona allo stesso modo dell'esempio precedente. Da qui il termine [non invadente] per questa tecnica.

Creeremo le due viste seguenti per illustrare la gestione dei link all'interno di una vista:

  • In [1] e [2] abbiamo due link di navigazione;
  • in [3] c'è un link di azione che invia il modulo. Non viene utilizzato per la navigazione.

La pagina 1 è generata dalla seguente vista [Action16Get.cshtml]:


@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action16Get</title>
  <script>
    function postForm() {
      // on récupère le formulaire du document
      var form = document.forms[0];
      // soumission
      form.submit();
    }
  </script>
</head>
<body>
  <h3>Navigation - page 1</h3>
  <h4>@ViewBag.info</h4>
  @using (Html.BeginForm("Action16Post", "Second"))
  {
    @Html.Label("data", "Tapez un texte")
    @Html.TextBox("data")
    <a href="javascript:postForm()">Valider</a>
  }
  <p>
    @Html.ActionLink("Page 2", "Action17Get", "Second")
  </p>
</body>
</html>

  • riga 22: informazioni inizializzate dall'azione che renderà la vista;
  • righe 23–28: un modulo;
  • riga 25: un'etichetta per il campo [data];
  • riga 26: un campo di immissione denominato [data];
  • riga 27: un link [submit]. Quando viene cliccato, viene eseguita la funzione JavaScript [postForm] (attributo href). Questa funzione è definita alle righe 12–17;
  • riga 14: viene recuperato un riferimento al primo modulo nel documento, quello alla riga 23;
  • riga 16: questo modulo viene inviato. In definitiva, si comporta come se fosse stato cliccato un pulsante [submit]. Il modulo viene inviato al controller e all'azione specificati alla riga 23;
  • riga 30: un link di navigazione. Il codice HTML generato è il seguente:


    <a href="/Second/Action17Get">Page 2</a>

Il metodo utilizzato è ActionLink(Text, Action, Controller).

La pagina 2 viene generata dalla seguente vista [Action17Get.cshtml]:


@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action17Get</title>
</head>
<body>
  <h3>Navigation - Page 2</h3>
  <h4>@ViewBag.info</h4>
  <p>
    @Html.ActionLink("Page 1", "Action16Get", "Second")
  </p>
</body>
</html>

Le azioni che generano queste visualizzazioni sono le seguenti:


      // Action16-GET
      [HttpGet]
      public ViewResult Action16Get()
      {
        ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
        return View("Action16Get");
      }
 
      // Action16-POST
      [HttpPost]
      public ViewResult Action16Post(string data)
      {
        ViewBag.info = string.Format("Contrôleur={0}, Action={1}, Data={2}", RouteData.Values["controller"], RouteData.Values["action"], data);
        return View("Action16Get");
      }
 
      // Action17-GET
      [HttpGet]
      public ViewResult Action17Get()
      {
        ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
        return View();
}

  • riga 6, l'azione [Action16Get] rende la vista [Action16Get.cshtml], ovvero la pagina 1 dell'esempio. Questa vista si basa sul [ViewBag] (riga 5);
  • riga 19: l'azione [Action17Get] genera la vista [Action17Get.cshtml], ovvero la pagina 2 dell'esempio. Questa vista si basa su [ViewBag] (riga 21);
  • riga 11: l'azione [Action16Post] elabora la richiesta POST proveniente dal modulo nella vista [Action16Get.cshtml]. Riceve il parametro denominato [data]. Ricordiamo che questo è il nome del campo di immissione nel modulo;
  • riga 13: le informazioni vengono inserite nel [ViewBag];
  • riga 14: viene visualizzata la vista [Action16Get.cshtml].

Si invitano i lettori a provare questo esempio.