Skip to content

5. Die Ansicht und ihr Modell

5.1. Einleitung

Kehren wir zur Architektur einer ASP.NET-MVC-Anwendung zurück:

Im vorigen Kapitel haben wir untersucht, wie ASP.NET MVC einer Aktion [2a] Anforderungsinformationen [1] in Form eines Modells übermittelt, das Validierungsbeschränkungen enthalten kann. Dieses Modell wurde als Eingabe an die Aktion übergeben, und wir bezeichneten es als Aktionsmodell. Wir konzentrieren uns nun auf das häufigste Ergebnis einer Aktion, den Typ „ “ [ViewResult], der einer Ansicht V [3] entspricht, die von ihrem Modell M [2c] begleitet wird. Dieses Modell wird als Ansichtsmodell V bezeichnet und ist nicht mit dem soeben behandelten Aktionsmodell zu verwechseln. Das eine ist die Eingabe für die Aktion, das andere die Ausgabe.

Beginnen wir damit, ein neues Projekt [Beispiel-03] [1] innerhalb derselben Lösung vom Typ „ASP.NET MVC Basic“ zu erstellen:

Erstellen wir einen Controller mit dem Namen [First] [2]. Der für diesen Controller generierte Code lautet wie folgt:


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

  • Zeilen 7–10: Es wurde eine [Index]-Aktion erstellt. Der Rückgabetyp der [Index]-Methode ist der der Klasse [ActionResult], von der die meisten möglichen Aktionsergebnisse abgeleitet sind;
  • Zeile 9: Die [View]-Methode der [Controller]-Klasse (Zeile 5) gibt einen [ViewResult]-Typ zurück, der von [ActionResult] abgeleitet ist. Diese Methode unterstützt zahlreiche Überladungen. Wir werden uns einige davon ansehen. Die wichtigste lautet wie folgt:

Image

  • Der erste Parameter ist der Name der Ansicht. Wird er weggelassen, wird die Ansicht verwendet, die denselben Namen wie die Aktion hat, die das [ViewResult] erzeugt, und sie wird im Ordner [/Views/{controller}] gesucht, wobei {controller} der Name des Controllers ist;
  • Der zweite Parameter ist die View-Vorlage. Wird er weggelassen, hat die Ansicht keine Vorlage.

Die folgende [Index]-Methode:


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

fordert an, dass die Ansicht [ /Views/First/Index.cshtml ] angezeigt wird. Es wird keine Vorlage an sie übergeben. Erstellen wir [1] den Ordner [/Views/First]:

und erstellen Sie dann die Ansicht [Index] [2] darin:

Wir geben den Namen der Ansicht in [3] an. Sie wird in [4] erstellt. Der generierte Code lautet wie folgt:


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

Dies ist Standard-HTML, mit Ausnahme der Zeilen 1–3, die C#-Code enthalten. Das Programm, das die Ansichten verwaltet, wird als View-Engine bezeichnet. Es verarbeitet alles, was kein HTML ist, und wandelt es in HTML um. Letztendlich wird genau das an den Browser des Clients gesendet. Die hier verwendete View-Engine ist [Razor]. Sie ermöglicht es Ihnen, C#-Code in eine Ansicht einzubinden. [Razor] interpretiert diesen C#-Code und generiert daraus HTML. Hier sind einige grundlegende Regeln für das Einfügen von C#-Code in eine Ansicht:

  • Der Übergang von HTML zu C# erfolgt, wenn das Zeichen @ auftritt (Zeile 1). Wenn dieses Zeichen einen Codeblock einleitet, werden geschweifte Klammern verwendet (Zeilen 1 und 3). Wenn es eine Variable einleitet, deren Wert Sie abrufen möchten, schreiben Sie einfach @variable;
  • Der Übergang von C# zu HTML erfolgt, sobald das Zeichen < auftritt (Zeile 5). Manchmal musst du diesen Übergang erzwingen, insbesondere wenn du reinen Text ohne HTML-Tags in die Seite einfügst. Verwende in diesem Fall das <text>-Tag, um den Text einzufügen: <text>reiner Text hier</text>.

Zeile 2 oben zeigt an, dass die Ansicht [Index] keine Masterseite hat.

Ändern wir die Ansicht wie folgt:


@{
  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>

  • Zeile 3: definiert eine C#-Variable;
  • Zeile 15: zeigt den Wert dieser Variablen an.

Rufen wir nun die URL [/First/Index] auf:

Image

Der empfangene HTML-Code lautet wie folgt:

<!DOCTYPE html>

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

Dies ist ein reines HTML-Dokument. Der gesamte C#-Code wurde entfernt.

5.2. Verwendung von [ViewBag] zur Übergabe von Informationen an die Ansicht

Wir erstellen eine neue Aktion namens [Action01], die mit der Ansicht [Action01.cshtml] verknüpft ist:

Die Aktion [Action01] sieht wie folgt aus:


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

  • Zeile 4: Wir verwenden die [ViewBag]-Eigenschaft des Controllers. Dies ist ein dynamisches Objekt, dem Eigenschaften hinzugefügt werden können, wie in Zeile 4 gezeigt. Ein wesentliches Merkmal dieses Objekts ist, dass es auch für die Ansicht zugänglich ist. Es ist daher eine Möglichkeit, Informationen an die Ansicht zu übergeben;
  • Zeile 5: Die Standardansicht für die Aktion wird angefordert. Dies ist die Ansicht [/First/Action01.cshtml]. Es wird kein Modell an sie übergeben.

Die Ansicht [Action01.cshtml] sieht wie folgt aus:


@{
  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>

  • Zeile 14: Die Eigenschaft [ViewBag.info] wird angezeigt.

Probieren wir es aus. Wir rufen die URL [/First/Action01] auf:

Image

5.3. Verwendung eines stark typisierten Modells zur Übergabe von Informationen an die Ansicht

Die vorherige Methode hat den Nachteil, dass sie keine Fehlererkennung vor der Ausführung zulässt. Wenn also die Ansicht [Action01.cshtml] den Code


<h4>@ViewBag.Info</h4>

, tritt ein Fehler auf, da die Eigenschaft [Info] nicht existiert. Die von der Aktion [Action01] erstellte Eigenschaft heißt [info]. Wir können daher ein stark typisiertes Modell verwenden, um dieses Problem zu vermeiden.

In einem der zuvor besprochenen Beispiele lautete die Aktion wie folgt:


    // 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);
}

Die Aktion [Action10] übergab sechs Informationen (E-Mail, Tag, Info1, Info2, Info3, Fehler) als Zeichenfolge an ihren Client. Wir werden diese Informationen an ein View-Modell [ViewModel01] übergeben. Da dieses Modell Informationen aus [ActionModel03] verwendet, werden wir es von dieser Klasse ableiten.

Zunächst kopieren wir [ActionModel03] aus dem Projekt [Example-02] in das aktuelle Projekt [Example-03]:

und ändern seinen Namespace so, dass er mit dem des Projekts [Example-03] übereinstimmt:


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; }
  }
}

  • Zeile 2: der neue Namespace;

Dann erstellen wir die Klasse [ViewModel01]:

Der Code für [ViewModel01] lautet wie folgt:


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

  • Zeile 3: Die Klasse erbt von [ActionModel03] und damit die Eigenschaften [Email, Day, Info1, Info2, Info3];
  • Zeile 5: Wir fügen ihr die Eigenschaft [Errors] hinzu.

Wir schreiben nun die Aktion [Action02], die:

  • das Aktionsmodell [ActionModel03] als Eingabe verwendet;
  • und das View-Modell [ViewModel01] zurückgibt.

Der Code lautet wie folgt:


    // 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});
}

  • Zeile 1: [Action02] empfängt das Aktionsmodell [ActionModel03]. Es gibt ein Ergebnis vom Typ [ViewResult] zurück;
  • Zeile 4: Fehler im Zusammenhang mit dem Aktionsmodell [ActionModel03] werden in der Zeichenkette [errors] zusammengefasst. Die Methode [getErrorMessagesFor] wurde auf Seite 60 beschrieben und in den Controller [First] des neuen Projekts aufgenommen;
  • Zeile 5: Die Methode [View] wird mit einem Parameter aufgerufen. Dies ist das View-Modell. Die Ansicht selbst wird nicht angegeben. Daher wird die Standardansicht [/Views/First/Action02] verwendet. Das View-Modell [ViewModel01] wird instanziiert und mit den fünf Informationen aus dem Aktionsmodell [ActionModel03] sowie den in Zeile 4 erstellten [errors]-Informationen initialisiert.

Wir erstellen nun die Ansicht [/First/Action02.cshtml]:

Der Code lautet wie folgt:


@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>

  • Die neue Funktion befindet sich in Zeile 1. Die Notation [@model] legt den Typ des View-Modells fest. Auf dieses Modell wird dann durch die Notation [@Model] verwiesen (Zeilen 16–21);
  • Zeilen 15–22: Die Informationen des Modells werden in einer Liste angezeigt.

Sehen wir uns einige Beispiele für die Ausführung der Aktion [Action02] an.

Zunächst ohne Parameter:

Image

dann mit falschen Parametern:

Image

dann mit korrekten Parametern:

Image

In diesem Beispiel nutzt das View-Modell [ViewModel01] die Informationen aus dem Action-Modell [ActionModel03]. Dies ist häufig der Fall. Wir können dann ein einziges Modell verwenden, das sowohl als Action-Modell als auch als View-Modell dient. Wir erstellen ein neues Modell [ActionModel04]:

Image

das wie folgt aussehen wird:


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; }
  }
}

  • Zeilen 8–28: das Aktionsmodell mit seinen Integritätsbeschränkungen. Diese Felder werden ebenfalls Teil der Ansicht sein;
  • Zeile 31: eine für das View-Modell spezifische Eigenschaft. Sie wurde durch die Anmerkung in Zeile 5 aus dem Aktionsmodell ausgeschlossen.

Wir erstellen die folgende neue Aktion [Action03]:


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

  • Zeile 2: [Action03] empfängt das Aktionsmodell vom Typ [ActionModel04];
  • Zeile 5: und gibt dasselbe Modell als View-Modell zurück;
  • Zeile 4: ergänzt um die [Errors]-Informationen;

Jetzt muss nur noch die Ansicht [/First/Action03.cshtml] erstellt werden:

  • in [1]: Klicken Sie mit der rechten Maustaste in den Code von [Action03] und wählen Sie dann [Ansicht hinzufügen];
  • in [2]: den Standardnamen der Ansicht;
  • in [3]: Geben Sie an, dass wir eine stark typisierte Ansicht erstellen;
  • in [4]: Wählen Sie die richtige Klasse aus der Dropdown-Liste aus, in diesem Fall die Klasse [ActionModel04];
  • in [5]: die erstellte Ansicht.

Wir geben der Ansicht [Action03] denselben Code wie der Ansicht [Action02]. Nur die Ansichtsvorlage (Zeile 1) und der Seitentitel (Zeile 11) ändern sich:


@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>

Rufen wir nun die Aktion [Action03] ohne Parameter auf:

Image

Die Ergebnisse sind dieselben wie zuvor. Es ist üblich, für die Aktion und die Ansicht dasselbe Modell zu verwenden, da das Ansichtsmodell häufig Informationen aus dem Aktionsmodell wiederverwendet. Wir verwenden daher ein umfassenderes Modell, das sowohl von der Aktion als auch von der dadurch generierten Ansicht genutzt werden kann. Wir müssen darauf achten, Informationen, die nicht zum Aktionsmodell gehören, aus der Datenbindung auszuschließen. Andernfalls könnte ein versierter Benutzer Teile des Ansichtsmodells ohne unser Wissen initialisieren.

5.4. [Razor] – Erste Schritte

Wir werden nun einige Elemente von [Razor]-Ansichten vorstellen, vor allem die foreach- und if-Anweisungen.

Angenommen, wir möchten eine Liste von Personen in einer HTML-Tabelle anzeigen. Das View-Modell könnte wie folgt aussehen [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; }
  }
}
  • Die Modellansicht ist die Klasse [ViewModel02], Zeilen 3–10;
  • Zeile 5: Das Modell verfügt über ein Array von Personen vom Typ [Person], das in den Zeilen 12–16 definiert ist;
  • Zeilen 6–10: Der Konstruktor des Modells initialisiert die Eigenschaft [People] aus Zeile 5 mit einem Array von zwei Personen.

Die Aktion, die dieses Modell als Ausgabe erzeugt, ist die folgende [Action04]:


    // Action04
    public ViewResult Action04()
    {
      return View(new ViewModel02());
}
  • Zeile 2: Die Aktion hat kein Eingabemodell;
  • Zeile 4: Sie übergibt ihre Standardansicht, eine Instanz des soeben definierten Modells [ViewModel02].

Die Ansicht [Action04.cshtml] zeigt das Modell [ViewModel02] an:

Der Code für die Ansicht [Action04.cshtml] lautet wie folgt:


@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>

  • Zeile 1: die View-Vorlage;
  • Zeile 2: Import des Namensraums für die in Zeile 24 verwendete Klasse [Person];
  • Zeilen 16–32: die HTML-Tabelle, die die Personen aus dem Modell anzeigt;
  • Zeile 24: Der Beginn des C#-Codes wird durch das Zeichen @ markiert. Die Anweisung [foreach] durchläuft alle Personen im Modell;
  • Zeilen 26–27: Das Zeichen < beendet den C#-Code und leitet den HTML-Code ein. Dann erscheint das Zeichen @ erneut, um zurück zu C# zu wechseln und den Namen der Person zu schreiben. Anschließend erscheint das Zeichen < erneut, um wieder in den HTML-Modus zu wechseln;
  • Zeile 28: Das Alter der Person wird ausgegeben.

Die Ausführung der Aktion [Action04] liefert das folgende Ergebnis:

Image

Andere Elemente einer Ansicht können durch eine Sammlung gefüllt werden: Listen (Dropdown-Listen oder andere), Optionsfelder und Kontrollkästchen. Betrachten Sie das folgende neue Beispiel, das eine Dropdown-Liste anzeigt.

Das Modell [ViewModel05] sieht wie folgt aus:


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; }
  }
}

  • Zeile 18: eine [Person2]-Klasse mit drei Eigenschaften;
  • Zeile 3: das Modell [ViewModel05] für die Ansicht;
  • Zeile 5: die Liste der Personen, die in der Dropdown-Liste im Format [Vorname Nachname] angezeigt werden sollen;
  • Zeile 6: die [Id] der Person, die aus der Dropdown-Liste ausgewählt werden soll;
  • Zeilen 8–16: der Konstruktor, der ein Array mit drei Personen erstellt (Zeilen 10–13) und die [Id] der Person festlegt, die als ausgewählt angezeigt werden soll.

Die Ansicht [Action05.cshtml] zeigt diese Vorlage an:

Der Code lautet wie folgt:


@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>

Die Eigenschaften der HTML-Dropdown-Liste wurden in Abschnitt 2.5.2.6 vorgestellt. Lassen Sie uns diese noch einmal zusammenfassen:

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

Image

HTML-Tag
<select size=".." name="..">
<option [selected="selected"] value=”v”>...</option>
...
</select>
zeigt den Text zwischen den Tags <option>...</option> in einer Liste an
Attribute
name="cmbValeurs": Name des Steuerelements.
size="1": Anzahl der sichtbaren Listenelemente. size="1" macht die Liste zu einem Kombinationsfeld.
selected="selected": Wenn dieses Schlüsselwort für ein Listenelement vorhanden ist, erscheint dieses Element in der Liste als ausgewählt. In unserem obigen Beispiel erscheint das Listenelement choice2 bei der ersten Anzeige als ausgewähltes Element im Kombinationsfeld.
value=”v”: Wenn der Eintrag vom Benutzer ausgewählt wird, wird dieser Wert [v] an den Server gesendet. Fehlt dieses Attribut, wird der angezeigte und ausgewählte Text an den Server gesendet.

Der Code in den Zeilen 17–25 generiert die <option>-Tags, die innerhalb des <select>-Tags in Zeile 16 platziert werden.

  • Zeile 17: Wir durchlaufen die Liste der Personen im Modell;
  • Zeile 20: Wir prüfen, ob die aktuelle Person diejenige ist, die ausgewählt werden soll. Wenn ja, bereiten wir den Text selected="selected" vor, der in das <option>-Tag eingefügt werden soll;
  • Zeile 24: Das <option>-Tag wird geschrieben.

Nennen wir die Aktion [Action05]:

  • in [1,2] werden die Personen im Format [Vorname Nachname] angezeigt;
  • in [1,2] ist die ausgewählte Person diejenige mit einer [Id] gleich 2.

Sehen wir uns nun den HTML-Quellcode der obigen Seite an:


<!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>

  • Zeilen 10–12: die drei vom [Razor]-Code generierten <option>-Tags;
  • Zeile 11: Die Person mit [Id]=2 wurde tatsächlich ausgewählt.

Die beiden obigen Beispiele reichen aus. Beim Schreiben einer [Razor]-Ansicht müssen Sie der Versuchung widerstehen, Logik darin unterzubringen. Der C#-Code würde uns dies zwar ermöglichen. Im MVC-Modell muss die Logik jedoch in der Aktion oder in den unteren Schichten [Business Logic, DAO] liegen, nicht in der Ansicht. Selbst wenn Sie sich an das MVC-Muster halten, kann es vorkommen, dass Sie am Ende viel Logik in der Ansicht haben, um Zwischenwerte zu berechnen. Dies kann bedeuten, dass das verwendete Modell nicht detailliert genug ist. Das Modell muss die Endwerte enthalten, die die Ansicht benötigt, damit diese sie nicht selbst berechnen muss. Eine gute Ansicht zeichnet sich durch minimale Logik und eine klare HTML-Struktur aus. Wenn zu viel C#-Code eingefügt wird, kann die HTML-Struktur unlesbar werden.

Im obigen Beispiel könnte die Dropdown-Liste von einem Benutzer verwendet werden, und wir würden dann wissen wollen, welche Person er ausgewählt hat. Dazu benötigen wir ein Formular.

5.5. Formulare – Erste Schritte

Das Formular, das dem Benutzer angezeigt wird, sieht wie folgt aus:

Image

Als View-Modell wird das zuvor verwendete Modell [ViewModel05] dienen. Die Aktion, die diese Ansicht anzeigt, sieht wie folgt aus:


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

  • Zeile 2: Die Aktion kann nur über eine HTTP-GET-Anfrage aufgerufen werden;
  • Zeile 5: Die Ansicht [/First/Action06Get.cshtml] wird unter Verwendung einer Instanz vom Typ [ViewModel05] als Modell angezeigt.

Die Ansicht [/First/Action06Get.cshtml] sieht wie folgt aus:


@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>

Die wichtigsten neuen Funktionen sind folgende:

  • Zeile 18: Damit der Browser die vom Benutzer eingegebenen Informationen übermitteln kann, benötigen wir ein Formular. Es wird durch die Tags <form> in den Zeilen 18 und 31 begrenzt.

Das HTML-Tag <form> wurde in Abschnitt 2.5.2.1 vorgestellt. Lassen Sie uns seine Eigenschaften noch einmal betrachten:

Formular

<form method="post" action="FormulairePost.aspx">
HTML-Tag
<form name="..." method="..." action="...">...</form>
Attribute
name="frmexample": Formularname – optional
method="..." : Methode, die vom Browser verwendet wird, um die im Formular gesammelten Werte an den Webserver zu senden
action="..." : URL, an die die im Formular gesammelten Werte gesendet werden.
Ein Webformular wird von den Tags <form>...</form> umschlossen. Das Formular kann einen Namen haben (name="xx"). Dies gilt für alle Steuerelemente, die sich innerhalb eines Formulars befinden. Der Zweck eines Formulars besteht darin, vom Benutzer über die Tastatur oder die Maus eingegebene Informationen zu erfassen und an eine Webserver-URL zu senden. An welche? An diejenige, auf die im Attribut action="URL" verwiesen wird. Fehlt dieses Attribut, werden die Informationen an die URL des Dokuments gesendet, das das Formular enthält. Ein Web-Client kann zwei verschiedene Methoden, nämlich POST und GET, verwenden, um Daten an einen Webserver zu senden. Das Attribut method="method" im <form>-Tag, wobei method auf GET oder POST gesetzt ist, teilt dem Browser mit, welche Methode er verwenden soll, um die im Formular gesammelten Informationen an die durch das Attribut action="URL" angegebene URL zu senden. Wenn das method-Attribut nicht angegeben ist, wird standardmäßig die GET-Methode verwendet.
  • Zeile 18: Wir sehen, dass die Formularwerte über eine HTTP-POST-Anfrage an die URL [/First/Action06] gesendet werden;
  • Zeile 30: Ein Formular muss über eine [submit]-Schaltfläche verfügen. Diese Schaltfläche löst die Übermittlung der eingegebenen Werte an die durch das [action]-Attribut des <form>-Tags angegebene URL aus.

Was genau sendet der Browser, wenn der Benutzer auf die Schaltfläche [Submit] klickt? Dies wurde in Abschnitt 2.5.3.1 erläutert. Fassen wir zusammen, was gesagt wurde:


HTML-Steuerelement


Visueller


zurückgegebene Werte

<input type="radio" value="Ja" name="R1"/>Ja
<input type="radio" name="R1" value="Nein" checked="checked"/>Nein
R1=Ja
– der Wert des value-Attributs des vom Benutzer ausgewählten Optionsfelds.
<input type="checkbox" name="C1" value="one"/>1
<input type="checkbox" name="C2" value="zwei" checked="checked"/>2
<input type="checkbox" name="C3" value="three"/>3
C1=eins
C2=zwei
- Werte der value-Attribute der vom Benutzer ausgewählten Kontrollkästchen
<input type="text" name="txtInput" size="20" value="ein paar Worte"/>
txtInput=Web+Programmierung
- vom Benutzer in das Eingabefeld eingegebener Text. Leerzeichen wurden durch das +-Zeichen ersetzt
<input type="password" name="txtMdp" size="20" value="aPassword"/>
txtPassword=thisIsSecret
- vom Benutzer in das Eingabefeld eingegebener Text
<textarea rows="2" name="inputArea" cols="20">
Zeile1
Zeile2
Zeile 3
</textarea>
inputField=die+Grundlagen+des+Webs%0D%0A
Web-Programmierung
- vom Benutzer in das Eingabefeld eingegebener Text. %OD%OA ist das Zeichen für Zeilenende. Leerzeichen wurden durch das +-Zeichen ersetzt
<select size="1" name="cmbValues">
<option value='1'>Auswahl1</option>
<option selected="selected" value='2'>Auswahl 2</option>
<option value='3'>Auswahl 3</option>
</select>
cmbValues=3
- Das Attribut [value] des vom Benutzer ausgewählten Elements
<select size="3" name="lst1">
<option selected="selected" value='1'>list1</option>
<option value='2'>Liste2</option>
<option value='3'>Liste3</option>
<option value='4'>Liste4</option>
<option value='5'>Liste5</option>
</select>
lst1=3
- [value]-Attribut des vom Benutzer ausgewählten Elements
<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
- [value]-Attribute der vom Benutzer ausgewählten Elemente
<input type="submit" value="Absenden" name="cmdSubmit"/>
 
cmdRenvoyer=Absenden
- Name- und Wert-Attribut der Schaltfläche, die zum Senden der Formulardaten an den Server verwendet wird
<input type="hidden" name="secret" value="aValue"/>
 
secret=aValue
- value-Attribut des versteckten Feldes

In unserem Formular gibt es zwei Tags, die einen Wert senden können:


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

und


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

Wenn der Benutzer Person Nr. 2 auswählt, werden die Werte im folgenden Format übermittelt:

personneId=2&valider=Valider

Die Parameternamen entsprechen den [name]-Attributen der am POST-Vorgang beteiligten Tags. Ohne dieses Attribut senden die Tags keinen Wert. Im obigen Beispiel könnten wir daher das Attribut name="valider" vom [submit]-Button weglassen. Der gesendete Wert ist das [value]-Attribut des Buttons. Diese Information ist für uns hier nicht relevant. Manchmal haben Formulare mehrere [submit]-Schaltflächen. In solchen Fällen ist es wichtig zu wissen, welche Schaltfläche angeklickt wurde. Wir werden daher den verschiedenen Schaltflächen das [name]-Attribut zuweisen.

Das <select>-Tag besteht aus einer Reihe von <option>-Tags:


    <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>

Der Wert des Attributs [value] der ausgewählten Option wird übermittelt. Fehlt dieses Attribut, wird der von der Option angezeigte Text – zum Beispiel [Pierre Martino] – übermittelt.

Die Zeichenfolge

personneId=2&valider=Valider

wird an die folgende URL [/First/Action06] gesendet:


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

Vielleicht erinnern Sie sich, dass wir bereits eine Aktion [Action06] hatten:


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

Es ist möglich, zwei Aktionen mit demselben Namen zu haben, sofern sie nicht dieselben HTTP-Anfragen bearbeiten:

  • [Action06] in Zeile 3 verarbeitet einen POST (Zeile 2);
  • [Action06] in Zeile c verarbeitet einen GET (Zeile b).

Die Aktion [Action06], die die POST-Anfrage verarbeitet, erhält die folgende Parameterzeichenfolge:

personneId=2&valider=Valider

Wir benötigen ein Aktionsmodell, um diese Werte zu kapseln. Dies wird das folgende [ActionModel06]-Modell sein:


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; }
  }
}

Die Aktion [Action06] empfängt dieses Modell und leitet es unverändert an die folgende Ansicht [Action06Post] weiter (Zeile 5 der Aktion):


@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>

Das Modell wird in den Zeilen 18 und 19 angezeigt.

Schauen wir uns ein Beispiel an:

In [1] wählen wir die dritte Person mit der [Id] 3 aus. In [2] senden wir das Formular ab. In [3] sehen wir die empfangenen Werte. In [4,5] sehen wir, dass dieselbe URL aufgerufen wurde, einmal über GET [4], das andere Mal über POST [5]. Dies ist in der URL nicht erkennbar.

In der nach dem POST angezeigten Ansicht möchten wir vielleicht lieber den Vor- und Nachnamen der ausgewählten Person sehen als deren Nummer. Wir müssen daher die POST-Ansicht und ihr Modell aktualisieren.

Wir erstellen eine Aktion [Action07], um diesen Fall zu behandeln. Diese Aktion muss die Sitzung des Benutzers nutzen, um die Liste der Personen zu speichern. Wir folgen dabei dem in Abschnitt 4.10 beschriebenen Muster, das es uns ermöglicht, Daten aus den Bereichen [Application] und [Session] in das Modell der Aktion einzubinden.

Das Sitzungsmodell wird die folgende Klasse [SessionModel] sein:


namespace Exemple_03.Models
{
  public class SessionModel
  {
    public Personne2[] Personnes { get; set; }
  }
}
  • Zeile 2: Die Sitzung speichert die Liste der Personen, die in der Dropdown-Liste angezeigt werden;

Wir müssen den vorherigen Typ [SessionModel] an einen Binder binden, den wir [SessionModelBinder] nennen werden. Dieser entspricht dem auf Seite 74 beschriebenen:

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"];
    }
  }
}

Die Bindung zwischen dem [SessionModel]-Modell und seinem [SessionModelBinder] ist in [Global.asax] definiert:


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();
    }
  }
  • Zeile 8: Das Modell wird in [Application_Start] an seinen Binder gebunden;
  • Zeile 13: Eine Instanz vom Typ [SessionModel] wird der Sitzung hinzugefügt, die mit dem Schlüssel [data] verknüpft ist.

Sobald dies geschehen ist, sieht die Aktion [Action07] wie folgt aus:


    // Action07-GET
    [HttpGet]
    public ViewResult Action07(SessionModel session)
    {
      ViewModel05 modèleVue = new ViewModel05();
      session.Personnes= modèleVue.Personnes;
      return View("Action07Get", modèleVue);
}
  • Zeile 3: Die Aktion ruft einen Typ [SessionModel] ab, d. h. die Daten des [Session]-Bereichs, die mit dem Schlüssel [data] verknüpft sind;
  • Zeile 5: Wir erstellen das View-Modell;
  • Zeile 6: Das Array „people“ wird in der Sitzung abgelegt. Wir benötigen es bei der nächsten Anfrage, der POST-Anfrage. Das HTTP-Protokoll ist ein zustandsloses Protokoll. Um den Zustand zwischen Anfragen aufrechtzuerhalten, muss eine Sitzung verwendet werden. Eine Sitzung ist benutzerspezifisch und wird vom Webserver verwaltet;
  • Zeile 7: Die Ansicht [Action07Get.cshtml] wird angezeigt. Sie sieht wie folgt aus:

@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>

Sie ist identisch mit der bereits untersuchten Ansicht [Action06Get.cshtml]. Der Hauptunterschied liegt in Zeile 7: der URL, an die die Formularwerte gesendet werden. Diese werden von der folgenden Aktion [Action07] verarbeitet:


    // 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);
}
  • Zeile 3: Die übermittelten Werte werden in das bereits zuvor (unten) verwendete Aktionsmodell [ActionModel06] gekapselt:

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; }
  }
}

  • Zeile 3: Der erste Parameter sind die [Session]-Scope-Daten, die mit dem Schlüssel [data] verknüpft sind;
  • Zeile 5: Eine LINQ-Abfrage ruft die Person mit der [Id] ab, die gesendet wurde;
  • Zeile 6: Wir erstellen die Zeichenfolge, die von der Ansicht [Action07Post] (Zeile 8) angezeigt werden soll;
  • Zeile 7: Um den richtigen [View]-Konstruktor aufzurufen, muss der Typ [string] in [object] umgewandelt werden.

Die Ansicht [Action07Post.cshtml] sieht wie folgt aus:


@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>

  • Zeile 1: Das Modell ist vom Typ [string];
  • Zeile 16: Die Zeichenfolge wird angezeigt.

Hier ist ein Ausführungsbeispiel:

Image

Image

5.6. Formular – ein vollständiges Beispiel

In Abschnitt 2.5.2.1 haben wir das folgende HTML-Formular betrachtet:

Image

Wir werden eine Aktion [Action08Get] untersuchen, die dieses Formular anzeigt (GET), sowie eine Aktion [Action08Post], die die vom Benutzer eingegebenen Werte verarbeitet (POST). Eine klassische Konfiguration.

Das obige View-Modell [1] ist eine Instanz der Klasse [ViewModel08]. Diese Klasse dient sowohl als

  • dem View-Modell, das durch eine GET-Anfrage an die Aktion [Action08Get] erzeugt wird;
  • das Modell für die Aktion [Action08Post] bei einer POST-Anfrage.

5.6.1. Das Modell im [Application]-Bereich

Wir gehen davon aus, dass die durch die Optionsfelder, Kontrollkästchen und verschiedenen Listen angezeigten Elemente Daten im [Application]-Bereich sind. Ein häufiges Szenario. Diese Informationen stammen aus einer Konfigurationsdatei oder einer Datenbank, auf die beim Start der Anwendung in der Methode [Application_Start] von [Global.asax] zugegriffen wird. Diese Methode entwickelt sich wie folgt:


    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();
}

  • Zeile 7: Der Typ [ApplicationModel], den wir in Kürze beschreiben werden, ist mit dem Datenbinder [ApplicationModelBinder] verknüpft, den wir bereits auf Seite 74 vorgestellt haben;
  • Zeile 10: Eine Instanz des Typs [ApplicationModel] wird im Anwendungswörterbuch gespeichert und dem Schlüssel [data] zugeordnet.

Die Klasse [ApplicationModel] dient dazu, alle Daten innerhalb des [Application]-Bereichs zu kapseln. Hier kapselt sie die Daten, die das Formular anzeigen muss:


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; }
    }
 
  }
}

  • Zeilen 45–49: das Formularsteuerelement, das die verschiedenen Sammlungen darstellt. [Label] ist der vom Formularsteuerelement angezeigte Text, [Value] ist der Wert, der von diesem Steuerelement übermittelt wird, wenn es ausgewählt wird;
  • Zeile 6: die vom Optionsfeld angezeigte Auswahl;
  • Zeile 7: die von den Kontrollkästchen angezeigte Sammlung;
  • Zeile 8: die von der Dropdown-Liste angezeigte Sammlung;
  • Zeile 9: die Sammlung, die von der Einfachauswahlliste angezeigt wird;
  • Zeile 10: die von der Mehrfachauswahlliste angezeigte Sammlung;
  • Zeilen 13–43: Diese Sammlungen werden durch den parameterlosen Konstruktor der Klasse initialisiert.

Die verschiedenen Sammlungen füllen das folgende Formular:

5.6.2. Das Modell für die Aktion [Action08Get]

Das vorherige Formular wird durch die folgende [Action08Get]-Aktion angezeigt:


    // 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));
}
  • Zeile 2: [Action08Get] reagiert nur auf eine [GET]-Anfrage;
  • Zeile 3: Es erhält als Parameter das soeben beschriebene Anwendungsmodell;
  • Zeile 5: Es initialisiert Daten im dynamischen Container [ViewBag];
  • Zeile 6: Es rendert die Ansicht [/First/Formulaire.cshtml] unter Verwendung des Modells [ViewModel08]. Dieses Modell ist dasjenige aus dem zuvor vorgestellten Formular. Dazu übergeben wir das Anwendungsmodell – das die anzuzeigenden Elemente definiert – an den Konstruktor.

5.6.3. Das [Form]-Ansichtsmodell

Die Klasse [ViewModel08] wird das Modell des Formulars sein. Diese Klasse sieht wie folgt aus:


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 einem Formular gibt es zwei Arten von Elementen: solche, die angezeigt werden, und solche, die eingegeben werden;
  • In den Zeilen 20–24 werden die anzuzeigenden Elemente definiert. Dabei handelt es sich um die verschiedenen Sammlungen des Formulars. Sie befinden sich im Anwendungsmodell (Zeilen 34–38);
  • Zeilen 10–17: definieren die Eingabefelder des Formulars;
  • Zeile 10: [RadioButtonField] ruft den Wert ab, der durch die folgenden Zeilen des Formulars übermittelt wird:


        <!-- 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>

Beachten Sie in den Zeilen 5 und 6, dass das Attribut [name] der beiden Optionsfelder den Namen der Eigenschaft angibt, die initialisiert wird. In den übermittelten Daten finden Sie eine Zeichenfolge in der Form:


param1=val1&RadioButtonField=2&param2=val2

, wenn der Benutzer die Option mit der Bezeichnung [no] ausgewählt hat. Tatsächlich wird das Attribut [value] der ausgewählten Option übermittelt.

  • Zeile 11: [CheckBoxesField] ruft die Werte ab, die durch die folgenden Zeilen des Formulars übermittelt wurden:


        <!-- 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>

Beachten Sie in den Zeilen 5 und 6, dass das Attribut [name] der Kontrollkästchen der Name der Eigenschaft ist, die initialisiert wird. In den gesendeten Daten finden Sie eine Zeichenfolge im folgenden Format:


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

, wenn der Benutzer die Kontrollkästchen mit den Bezeichnungen [2] und [3] aktiviert hat. Es ist das Attribut [value] der markierten Optionen, das übermittelt wird. Da mehrere Parameter mit demselben Namen übermittelt werden können, ist [CheckBoxesField] ein Array von Werten und kein einzelner Wert. Wenn keine Kontrollkästchen ausgewählt sind, fehlt der Parameter [CheckBoxesField] in der übermittelten Zeichenfolge, und die gleichnamige Modelleigenschaft wird nicht initialisiert. Dies kann problematisch sein, wie wir noch sehen werden.

  • Zeile 12: [TextField] ruft den Wert ab, der durch die folgenden Zeilen des Formulars übermittelt wird:


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

Zeile 5: Das Attribut [name] des Eingabefelds ist der Name der Eigenschaft, die initialisiert wird. In den gesendeten Daten finden Sie eine Zeichenfolge im folgenden Format:


param1=val1&TextField=abcdef&param2=val2

, wenn der Benutzer [abcdef] in das Eingabefeld eingegeben hat.

  • Zeile 13: [PasswordField] ruft den Wert ab, der durch die folgenden Zeilen des Formulars übermittelt wurde:


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

Zeile 5: Das Attribut [name] des Eingabefelds ist der Name der Eigenschaft, die initialisiert wird. In den übermittelten Daten finden Sie eine Zeichenfolge im folgenden Format:


param1=val1&PasswordField=abcdef&param2=val2

, wenn der Benutzer [abcdef] in das Eingabefeld eingegeben hat.

  • Zeile 14: [TextAreaField] ruft den Wert ab, der durch die folgenden Zeilen des Formulars übermittelt wurde:


        <!-- 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>

Zeile 5: Das Attribut [name] des Eingabefelds ist der Name der Eigenschaft, die initialisiert wird. In den übermittelten Daten finden Sie eine Zeichenfolge im folgenden Format:


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

, wenn der Benutzer [abcdef] gefolgt von einem Zeilenumbruch und [ijk] in das Eingabefeld eingegeben hat.

  • Zeile 15: [DropDownListField] ruft den Wert ab, der in den folgenden Zeilen des Formulars übermittelt wurde:


        <!-- 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>

Zeile 5: Das Attribut [name] des Tags <select> ist der Name der Eigenschaft, die initialisiert wird. In den gesendeten Daten finden Sie eine Zeichenfolge im folgenden Format:


param1=val1&DropDownListField=1&param2=val2

, wenn der Benutzer die Option [choice1] ausgewählt hat. Es wird das Attribut [value] der ausgewählten Option gesendet.

  • Zeile 16: [SingleChoiceListField] ruft den Wert ab, der durch die folgenden Zeilen des Formulars übermittelt wurde:


        <!-- 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>

Zeile 5: Das Attribut [name] des Tags <select> ist der Name der Eigenschaft, die initialisiert wird. Das Attribut [size="3"] stellt sicher, dass keine Dropdown-Liste angezeigt wird. In den gesendeten Daten finden wir eine Zeichenfolge im folgenden Format:


param1=val1&SimpleChoiceListField=3&param2=val2

, wenn der Benutzer die Option [liste3] ausgewählt hat. Es ist das Attribut [value] der ausgewählten Option, das gesendet wird. Der Parameter [SingleChoiceListField] fehlt möglicherweise in der gesendeten Zeichenfolge, wenn kein Element ausgewählt wurde.

  • Zeile 17: [MultipleChoiceListField] ruft die Werte ab, die in den folgenden Zeilen des Formulars übermittelt wurden:


        <!-- 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>

Zeile 5: Das Attribut [name] des Tags <select> ist der Name der Eigenschaft, die initialisiert wird. Das Attribut [size="3"] stellt sicher, dass keine Dropdown-Liste angezeigt wird, und das Attribut [multiple] ermöglicht es dem Benutzer, mehrere Elemente auszuwählen, indem er die [Strg]-Taste gedrückt hält. In den gesendeten Daten finden Sie eine Zeichenfolge im folgenden Format:


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

, wenn der Benutzer die Optionen [list1] und [list3] ausgewählt hat. Es ist das [value]-Attribut der ausgewählten Optionen, das gesendet wird. Da mehrere Parameter mit demselben Namen gesendet werden können, ist [MultipleChoiceListField] ein Array von Werten und kein einzelner Wert. Wenn kein Kontrollkästchen ausgewählt ist, fehlt der Parameter [MultipleChoiceListField] in der gesendeten Zeichenfolge, und die gleichnamige Modelleigenschaft wird nicht initialisiert.

Die verschiedenen zuvor vorgestellten Eingabefelder erhalten die vom Formular übermittelten Werte. Sie können auch vor dem Absenden des Formulars initialisiert werden. Genau das wurde hier getan:


      // 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" };

Wenn diese Werte nach einer POST-Anfrage aus dem Formular erhalten wurden, würde dies bedeuten, dass der Benutzer:

  • Zeile 2: die Option [nein] aus den Optionsfeldern ausgewählt;
  • Zeile 3: die Option [2] in den Kontrollkästchen ausgewählt hat;
  • Zeile 4: [ein paar Wörter] in das Eingabefeld eingegeben hat;
  • Zeile 5: [secret] als Passwort eingegeben hat;
  • Zeile 6: [Zeile1\nZeile2] in das mehrzeilige Eingabefeld eingegeben;
  • Zeile 7: die Option [Option2] aus der Dropdown-Liste ausgewählt;
  • Zeile 8: Option [list3] aus der Einfachauswahlliste ausgewählt;
  • Zeile 9: die Optionen [list1] und [list3] aus der Mehrfachauswahlliste ausgewählt;

Wir gehen so vor, als ob eine POST-Anfrage gestellt worden wäre und wir das Formular genau so zurückgeben wollten, wie es eingegeben wurde. Genau das passiert, wenn ein fehlerhaftes Formular an den Benutzer zurückgegeben wird. Das Formular wird genau so zurückgegeben, wie es eingegeben wurde.

5.6.4. Die Ansicht [Form]

Die Ansicht [/First/Formulaire.cshtml] zeigt das Formular an:


@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>
  • Zeile 1: [ViewModel08] ist das Formularmodell;
  • Zeile 12: das <form>-Tag für das Formular. Dieses Formular wird mit der [POST]-Methode (Methodenattribut) an die URL [/First/Action08Post] (Aktionsattribut) gesendet;
  • Zeile 33: die Schaltfläche [submit], die zum Absenden des Formulars verwendet wird;
  • Zeilen 22–27: Anzeige der Optionsfelder:

Image

  • Zeile 22: Wir durchlaufen die vom Radiobutton angezeigte Sammlung;
  • Zeile 24: Die Schaltfläche, deren [value]-Attribut dem Wert der [RadioButtonField]-Eigenschaft entspricht, muss markiert sein. Dazu muss sie das Attribut [checked="checked"] aufweisen;
  • Zeile 25: Erzeugt das <input type="radio">-Tag mit dem Wert [@item.Value] und der Beschriftung [@item.Label];
  • Zeile 26: Das <text/>-Tag ist kein anerkanntes HTML-Tag. Es ist für [Razor] vorgesehen. Wenn es auftritt, erzeugt [Razor] einen Zeilenumbruch. Dies hat keine Auswirkungen auf das angezeigte Formular, wirkt sich jedoch auf den generierten HTML-Code aus. Die <input type="radio">-Tags stehen dann auf zwei verschiedenen Zeilen statt auf derselben Zeile. Dies macht den Code lesbarer, wenn Sie den Quellcode der angezeigten Seite im Browser betrachten;

Sehen wir uns die anderen Elemente der Ansicht an:


        <!-- 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>
  • Zeile 6: Wir durchlaufen die von den Kontrollkästchen angezeigte Sammlung;
  • Zeile 8: Ein Kontrollkästchen, dessen [value]-Attribut einer der Werte der [CheckBoxesField]-Eigenschaft ist, muss aktiviert sein. Dazu muss es das Attribut [checked="checked"] aufweisen. Wir verwenden einen LINQ-Ausdruck, um festzustellen, ob ein Wert in einem Array enthalten ist;
  • Zeile 25: generiert das <input type="checkbox">-Tag mit dem Wert [@item.Value] und der Beschriftung [@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>
  • Zeilen 5, 12: Wir weisen dem Attribut [value] des Tags den Wert des Modells zu;
  • Zeile 19: wie oben, jedoch mit anderer Syntax.

        <!-- 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>
  • Zeile 7: Wir durchlaufen die von der Dropdown-Liste angezeigte Sammlung;
  • Zeile 9: Eine Option, deren [value]-Attribut mit dem Wert der [DropDownListField]-Eigenschaft übereinstimmt, muss dann ausgewählt werden. Dazu muss sie das Attribut [selected="selected"] aufweisen;
  • Zeile 25: Erzeugt das Tag <option value="value">label</option> mit dem Wert [@item.Value] und der Beschriftung [@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>

Die Erklärung ist dieselbe wie für die Dropdown-Liste.


        <!-- 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>
  • Zeile 7: Wir durchlaufen die von der Liste angezeigte Sammlung;
  • Zeile 9: Eine Option, deren [value]-Attribut einer der Werte der [MultipleChoiceListField]-Eigenschaft ist, muss ausgewählt werden. Dazu muss sie das Attribut [selected="selected"] aufweisen. Wir verwenden einen LINQ-Ausdruck, um festzustellen, ob ein Wert in einem Array enthalten ist;
  • Zeile 10: Generierung des Tags <option value="value">label</option> mit [@item.Value] als Wert und [@item.Label] als Bezeichnung;

5.6.5. Verarbeitung des Formular-POST

Wir haben gesehen, dass das Formular an die Aktion [Action08Post] gesendet wird:


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

Die Aktion [Action08Post] sieht wie folgt aus:


    // 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);
}
  • Zeile 3: Das Anwendungsmodell wird zusammen mit den übermittelten Werten als Parameter übergeben. Diese stehen im Typ [FormCollection] zur Verfügung. Der Wert des übermittelten Parameters [RadioButtonField] wird mit dem Ausdruck posted["RadioButtonField"] abgerufen. Dies gibt eine Zeichenfolge oder den Null-Zeiger zurück. Wenn Sie posted["CheckBoxesField"] schreiben, erhalten Sie ein Array von Zeichenfolgen oder den Null-Zeiger;
  • warum also nicht schreiben:

public ViewResult Action08Post(ApplicationModel application, ViewModel08 posted)

Dafür gibt es zwei Gründe:

  • Erstens wird das Framework das Modell [ViewModel08] mithilfe des parameterlosen Konstruktors instanziieren, was dazu führt, dass die Sammlungen des Modells nicht initialisiert werden;
  • Der zweite ist, dass wir kontrollieren wollen, was in das Modell gelangt. Wir wissen, dass es vier mögliche Quellen für das Modell gibt: die Parameter einer GET- oder POST-Anfrage, die verwendete Route und die Werte aus einer hochgeladenen Datei. Hier wollen wir das Modell nur mit den übermittelten Werten initialisieren.
  • Zeile 6: Wir instanziieren das Modell mit dem richtigen Konstruktor;
  • Zeile 7: Wir initialisieren es mit den übermittelten Werten. Nach diesem Vorgang entspricht das Modell der Eingabe des Benutzers;
  • Zeile 8: Wir zeigen das Formular erneut an. Der Benutzer sieht es genau so, wie es eingegeben wurde.

Schauen wir uns ein Beispiel an:

In [2] spiegelt das Ergebnis von [POST] genau wider, was in [1] eingegeben wurde.

5.6.6. Behandlung von POST-Fehlern

Wir haben erwähnt, dass, wenn für die Felder [CheckBoxesField, SimpleChoiceListField, MultipleChoiceListField] kein Wert angekreuzt oder ausgewählt wurde, die entsprechenden Parameter nicht Teil der gesendeten Zeichenfolge waren und daher die Modelleigenschaften mit denselben Namen nicht initialisiert wurden.

Betrachten wir das folgende Beispiel:

  • In [1] war kein Kontrollkästchen markiert;
  • in [2] gibt [POST] ein aktiviertes Kontrollkästchen zurück.

Die Erklärung lautet wie folgt:

  • Da keine Kontrollkästchen ausgewählt sind, ist der Parameter [CheckBoxesField] nicht in den gesendeten Werten enthalten;
  • Die Aktion [Action08Post] läuft wie folgt ab:

    [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);
}
  • Zeile 5: Das Formularmodell wird instanziiert. Der verwendete Konstruktor weist der Eigenschaft [CheckBoxesField] jedoch das Array ["2"] zu;
  • Zeile 6: Die übermittelten Werte werden im Modell gespeichert. Da der Parameter [CheckBoxesField] nicht zu den übermittelten Werten gehört, wird der gleichnamigen Eigenschaft kein Wert zugewiesen. Sie behält daher ihren Wert ["2"], was bedeutet, dass bei der Anzeige des Formulars das Kontrollkästchen Nr. 2 aktiviert ist, obwohl dies nicht der Fall sein sollte.

Dieses Problem lässt sich auf verschiedene Weise beheben. Wir entscheiden uns dafür, es im Code für die Aktion [Action08Post] zu beheben:


// 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);
    }
  • Zeilen 9–20: Wir prüfen, ob bestimmte Parameter übermittelt wurden oder nicht. Falls nicht, initialisieren wir sie mit dem Wert, der dem Fall entspricht, dass der Benutzer nichts eingegeben hat. Diese Prüfung wurde für die Dropdown-Liste nicht durchgeführt, da dort im Gegensatz zu den anderen Listen immer ein Element ausgewählt ist.

Leser sind eingeladen, diese neue Version zu testen.

5.7. Verwendung von Methoden zur Formularerstellung

5.7.1. Das neue Formular

Wir erstellen ein neues Formular [Form2.cshtml], das ein Formular generiert, das mit dem vorherigen identisch ist:

Sehen wir uns noch einmal den Code an, der zur Generierung der Dropdown-Liste des Formulars verwendet wurde:


        <!-- 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>

Dieser Code hat zwei Nachteile:

  • Der größte Nachteil ist, dass die Beschaffenheit der Komponente – in diesem Fall eine Dropdown-Liste – aufgrund der Komplexität des Codes verloren geht;
  • Zeile 5: Wenn Sie sich beim Namen der Modelleigenschaft, die als [name]-Attribut verwendet werden soll, vertippen, bemerken Sie dies erst zur Laufzeit.

ASP.NET MVC bietet spezielle Methoden namens [HTML-Helper], die, wie der Name schon sagt, dazu dienen, die HTML-Generierung zu vereinfachen, insbesondere für Formulare. Mit diesen Klassen wird das oben gezeigte Dropdown-Listen- wie folgt geschrieben:


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

Die Dropdown-Liste wird durch die Zeilen 4–5 generiert. Der Code ist deutlich weniger komplex. Der für die Dropdown-Liste generierte HTML-Code lautet wie folgt:


        <!-- 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>
  • Zeile 4: Das Attribut [name] ist korrekt;
  • Zeilen 4–6: Die Optionen werden korrekt generiert, und die richtige Option wurde ausgewählt.

Kehren wir zu dem Code zurück, der diese HTML-Zeilen generiert hat:


@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
  • Der erste Parameter ist eine Lambda-Funktion (so wird sie genannt), wobei `m` das View-Modell darstellt und `m.DropDownListField` eine Eigenschaft dieses Modells ist. Der HTML-Code-Generator verwendet den Namen dieser Eigenschaft, um die Attribute [id] und [name] des zu generierenden [select]-Elements zu erzeugen. Wird eine nicht vorhandene Eigenschaft verwendet, tritt der Fehler bereits zur Kompilierungszeit und nicht erst zur Laufzeit auf. Dies ist eine Verbesserung gegenüber der vorherigen Lösung, bei der Namensfehler erst zur Laufzeit erkannt wurden;
  • Der zweite Parameter dient zur Angabe der Sammlung von Elementen, die die Dropdown-Liste füllen sollen. Mit der [SelectList]-Klasse können Sie diese Sammlung erstellen:
    • Ihr erster Parameter ist eine beliebige Sammlung von Elementen. Hier haben wir eine Sammlung vom Typ [Item];
    • ihr zweiter Parameter ist die Eigenschaft der Elemente, die den Wert des <option>-Tags liefert. Hier ist es die [Value]-Eigenschaft der [Item]-Klasse;
    • ihr dritter Parameter ist die Eigenschaft der Elemente, die die Beschriftung für das <option>-Tag bereitstellt. Hier ist es die [Label]-Eigenschaft der [Item]-Klasse;
  • Um zu bestimmen, welche Option ausgewählt werden soll (das selected-Attribut), macht das Framework genau das, was wir tun: Es vergleicht den Wert der Option mit dem aktuellen Wert der [DropDownListField]-Eigenschaft.

Sehen wir uns nun die anderen Methoden an, die wir verwenden können:

Optionsfelder

Der neue Code lautet wie folgt:


        <!-- 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>

Der generierte HTML-Code lautet wie folgt:


        <!-- 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>

Die verwendete Methode ist [Html.RadioButtonFor]:

@Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)
  • Der erste Parameter ist die Modelleigenschaft, die dem Radiobutton zugeordnet wird (das [name]-Attribut);
  • der zweite Parameter ist der Wert, der dem Radiobutton zugewiesen werden soll (das [value]-Attribut).

Kontrollkästchen

Der Code ändert sich wie folgt:


        <!-- 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>

Die Methode zum Erstellen von Kontrollkästchen lautet [Html.CheckBoxFor]:

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

Der Parameter ist die boolesche Eigenschaft des Modells, die dem Kontrollkästchen zugeordnet wird. Wenn [Property=true] ist, wird das Kontrollkästchen aktiviert. Wenn [Property=false] ist, wird das Kontrollkästchen nicht aktiviert. In allen Fällen wird das Attribut [value] auf true gesetzt. Der generierte HTML-Code lautet wie folgt:


<input id="Propriété" name="Propriété" type="checkbox" value="true" />
<input name="Propriété" type="hidden" value="false" />
  • Zeile 1: das Kontrollkästchen mit dem Attribut [value="true"];
  • Zeile 2: ein verstecktes Feld (type=hidden) mit demselben Namen [Property] wie das Kontrollkästchen, mit dem Attribut [value="false"]. Warum gibt es zwei [input]-Tags mit demselben Namen? Es gibt zwei Fälle:
  • Das Kontrollkästchen in Zeile 1 ist aktiviert. Dann lautet die übermittelte Parameterzeichenfolge Property=true&Property=false (Zeilen 1 und 2). Da die Eigenschaft [Property] nur einen Wert erwartet, könnte man meinen, dass das Framework den Wert [true] der Eigenschaft [Property] zuweist. Dazu müsste es lediglich eine logische ODER-Verknüpfung zwischen den empfangenen Werten durchführen;
  • Das Kontrollkästchen in Zeile 1 ist nicht aktiviert. In diesem Fall lautet die übermittelte Parameterzeichenfolge Property=false (nur Zeile 2), und der Eigenschaft [Property] wird der Wert [false] zugewiesen, was korrekt ist (das Kontrollkästchen war nicht aktiviert).

Einzeiliges Eingabefeld

Der neue Code lautet wie folgt:


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

Der generierte HTML-Code lautet wie folgt:


          <!-- 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>

Die verwendete Methode lautet wie folgt:


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

  • Der erste Parameter gibt die Eigenschaft des Modells an, die dem Eingabefeld zugeordnet ist. Der Name der Eigenschaft wird in den Attributen [name] und [id] des generierten <input>-Tags verwendet, und ihr Wert wird dem Attribut [value] zugewiesen;
  • Der zweite Parameter ist eine anonyme Klasse, die bestimmte Attribute des generierten HTML-Tags festlegt, in diesem Fall das Attribut [size].

Passwort-Eingabefeld

Der neue Code lautet wie folgt:


        <!-- 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>

Der generierte HTML-Code lautet wie folgt:


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

Die verwendete Methode lautet wie folgt:


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

Das Verhalten ähnelt dem der Methode [Html.TextBoxFor].

Mehrzeiliges Eingabefeld

Der neue Code lautet wie folgt:


        <!-- 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>

Der generierte HTML-Code lautet wie folgt:


        <!-- 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>

Die verwendete Methode ist wie folgt:


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

Das Verhalten ähnelt dem der Methode [Html.TextBoxFor].

Einfachauswahlliste

Der neue Code lautet wie folgt:


        <!-- 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>

und der generierte HTML-Code lautet wie folgt:


        <!-- 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>

Wir haben die Methode [Html.DropDownListFor] bereits behandelt. Der einzige Unterschied besteht hier im dritten Parameter, der dazu dient, ein [size]-Attribut anzugeben, das nicht 1 ist. Durch diese Funktion wird eine Dropdown-Liste [size=1] in eine einfache Liste umgewandelt.

Die Mehrfachauswahlliste

Der neue Code lautet wie folgt:


        <!-- 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>

und der generierte HTML-Code lautet wie folgt:


        <!-- 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>

Die Methode


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

funktioniert wie die Methode [Html.DropDownListFor], mit dem Unterschied, dass sie eine Mehrfachauswahlliste generiert. Die ausgewählten Optionen sind diejenigen, deren Wert (value-Attribut) im Array [MultipleChoiceListField] enthalten ist.

Das form-Tag kann auch mithilfe einer Methode generiert werden:


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

Der generierte HTML-Code lautet wie folgt:


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

Die Methode


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

nimmt den Namen einer Aktion als ersten Parameter und den Namen eines Controllers als zweiten Parameter entgegen.

5.7.2. Die Aktionen und das Modell

Das Formular wird durch die folgende [Action09Get]-Aktion gerendert:


    // 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));
}

Die in Zeile 6 gerenderte Ansicht ist [Form2], die mit dem folgenden [ViewModel09]-Modell verknüpft ist:


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] unterscheidet sich von [ViewModel08] in der Handhabung der Kontrollkästchen. Anstelle eines Arrays mit drei Kontrollkästchen wurden drei separate Kontrollkästchen verwendet (Zeilen 11–13).

Das Formular wird durch die folgende [Action09Post]-Aktion verarbeitet:


    // 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);
}

Die Aktion [Action09Post] ist bis auf zwei Punkte identisch mit der Aktion [Action08Post]:

  • Zeile 18: Anstelle der Ansicht [Form] wird die Ansicht [Form2] verwendet;
  • Es erfolgt keine Behandlung von nicht markierten Kontrollkästchen mehr. Dies wird nun korrekt von der Methode [Html.CheckBoxFor] übernommen.

5.8. Erstellen eines Formulars aus den Metadaten des Modells

Neben den oben genannten Methoden gibt es noch weitere Möglichkeiten, ein Formular zu generieren. Eine davon besteht darin, einer Modelleigenschaft Informationen zuzuordnen, die dem MVC-Framework mitteilen, welches Input-Tag generiert werden soll. Diese Informationen werden als Metadaten bezeichnet.

Betrachten Sie das folgende View-Modell [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";
    }
  }
}

Die Metadaten bestehen aus den Tags [Display, DataType, UIHint].

Diese Ansichtsvorlage wird durch die folgende [Action10Get]-Aktion erstellt:


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

In Zeile 5 oben weisen wir die Standardansicht für die Aktion [/First/Action10Get.cshtml] an, das ViewModel vom Typ [ViewModel10] anzuzeigen. Diese Ansicht sieht wie folgt aus:


@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>

Für jede Eigenschaft des Modells verwenden wir die Methode:

  • Html.LabelFor, um den Wert der Metadaten [DisplayName] der Eigenschaft anzuzeigen;
  • Html.EditorFor, um das HTML-Input-Tag für den Eigenschaftswert zu generieren. Diese Methode verwendet die Metadaten [DataType] und [UIHint] der Eigenschaft;
  • Html.DisplayFor, um den Wert der Eigenschaft in dem durch die Metadaten [DataType] festgelegten Format anzuzeigen.

Hier ist ein Beispiel dafür, wie dies im Chrome-Browser funktioniert:

Image

Je nach verwendetem Browser können die Seiten unterschiedlich aussehen. Dies liegt daran, dass die generierte Ansicht die neuen Tags verwendet, die mit HTML Version 5, auch bekannt als HTML5, eingeführt wurden. Noch unterstützen nicht alle Browser diese Version. Im obigen Beispiel unterstützt der Chrome-Browser sie teilweise.

5.8.1. Das [POST] des Formulars

Der [POST] des Formulars wird von der folgenden [Action10Post]-Aktion verarbeitet:


    // 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);
}
  • Zeile 3: Die Aktion [Action10Post] verwendet das übermittelte Formular als Eingabemodell;
  • Zeile 5: Wir holen die Validierungsfehler aus diesem Formular ab;
  • Zeile 6: Die Textantwort an den Client wird vorbereitet;
  • Zeile 7: Wir senden sie ab.

Betrachten wir nun nacheinander die Eigenschaften des Modells [ViewModel10] und sehen wir uns an, wie die zugehörigen Metadaten den generierten HTML-Code und die Validierung der Eingabefelder beeinflussen.

5.8.2. Eigenschaft [Text]

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.LabelFor] hat in Zeile 2 das Tag <label> generiert. Der Wert des Attributs [for] ist der Name der Parametereigenschaft der Methode [Html.LabelFor]


public string Text { get; set; }

Der zwischen dem Start- und End-Tag angezeigte Text ist der Text der Metadaten


[Display(Name="Text")]

Die Methode [Html.LabelFor] funktioniert immer auf diese Weise. Wir werden darauf bei den anderen Eigenschaften nicht noch einmal eingehen.

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> generiert. Beachten Sie, dass es ein [class]-Attribut hat, das die CSS-Klasse [text-box single-line] mit dem Tag verknüpft. Die Attribute [id] und [name] haben den Wert [Text], was dem Namen der Parameter-Eigenschaft der Methode [Html.EditorFor] entspricht. Das Attribut [type] hat den Wert [text] aufgrund der Metadaten


[DataType(DataType.Text)]

  • Die Methode [Html.DisplayFor] hat den Text in Zeile 4 generiert. Dies ist der Wert der Parameter-Eigenschaft der Methode [Html.DisplayFor]. Diese Methode wird durch die Metadaten beeinflusst


[DataType(DataType.Text)]

beeinflusst, wodurch der Wert als unformatierter Text angezeigt wird.

5.8.3. Eigenschaft [MultiLineText]

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <textarea> generiert. Beachten Sie, dass es ein [class]-Attribut enthält, das die CSS-Klasse [text-box multi-line] mit dem Tag verknüpft. Die Attribute [id] und [name] haben den Wert [MultiLineText], was dem Namen der Parameter-Eigenschaft der Methode [Html.EditorFor] entspricht. Dies ist immer der Fall. Wir werden dies nicht erneut erwähnen. Das generierte Tag lautet <textarea> aufgrund der Metadaten


[DataType(DataType.MultilineText)]

, das angibt, dass es sich bei der Eigenschaft um mehrzeiligen Text handelt.

  • Die Methode [Html.DisplayFor] hat den Text für die Zeilen 4–5 generiert. Dies ist der Wert der Parameter-Eigenschaft der Methode [Html.DisplayFor].

5.8.4. [Number]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


<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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das <input>-Tag generiert, wobei das Attribut [type] auf [number] gesetzt wurde. Dies liegt offenbar einfach daran, dass die Eigenschaft den Typ [int] hat. Die Attribute [data-val], [data-val-number] und [data-val-required] werden von HTML5 nicht erkannt. Sie werden von einem clientseitigen JavaScript-Framework zur Datenvalidierung verwendet;
  • Die Methode [Html.DisplayFor] hat den Text in Zeile 4 generiert, der dem Wert der Eigenschaft entspricht.

Validierung

Die [data-x]-Attribute beeinflussen die clientseitige Datenvalidierung. Hier sind zwei Beispiele:

Wir geben eine falsche Zahl ein und senden das Formular ab:

Image

Im obigen Beispiel erfolgte die Validierung auf der Client-Seite. Das Formular wird erst abgeschickt, wenn der Fehler behoben wurde.

Ein weiteres Beispiel: Wir geben nichts ein:

In [1] oben meldet [Action10Post] einen Fehler. Sie erinnern sich vielleicht, dass wir dieses Verhalten zuvor durch die Verwendung des Attributs [Required] für die zu validierende Eigenschaft (siehe 63) erreicht haben, in diesem Fall die Eigenschaft [Number]. Hier war das nicht notwendig.

5.8.5. Eigenschaft [Decimal]

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [text] generiert. Die anderen Attribute sind identisch mit denen, die für die vorherige [Number]-Eigenschaft generiert wurden. Die Metadaten:


[UIHint("Decimal")]

bewirkt, dass der Eigenschaftswert sowohl bei der Methode [Html.EditorFor] als auch bei der Methode [Html.DisplayFor] mit zwei Dezimalstellen angezeigt wird

Validierung

Im Gegensatz zum vorherigen Fall werden auf der Clientseite keine Validierungsfehler gemeldet. Der Fehler wird nur von der Aktion [Action10Post] gemeldet. Auch hier ist die Dezimalzahl erforderlich, ohne dass das Attribut [Required] gesetzt werden muss.

5.8.6. [Tel]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [tel] generiert. Dieser Wert wurde auf Grundlage der Metadaten generiert:


[DataType(DataType.PhoneNumber)]

Der Typ [tel] für ein <input>-Tag ist eine neue Funktion in HTML5. Der Chrome-Browser behandelte es als ein <input>-Tag mit dem Typ [text].

Validierung

Es werden weder auf der Client- noch auf der Serverseite Validierungsfehler gemeldet. Sie können beliebige Inhalte eingeben.

5.8.7. Eigenschaft [Datum]

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [date] generiert. Dieser Wert wurde auf der Grundlage der Metadaten generiert:


[DataType(DataType.Date)]

Der Typ [date] für ein <input>-Tag ist eine neue Funktion in HTML5. Der Chrome-Browser erkennt ihn und ermöglicht die Eingabe des Datums über einen Kalender. Darüber hinaus wird das eingegebene Datum im Format [dd/mm/yyyy] angezeigt, was bedeutet, dass Chrome das Datumsformat an die [locale] des Browsers anpasst.

  • Die Methode [Html.DisplayFor] schrieb das Datum ebenfalls im Format [dd/mm/yyyy], wiederum aufgrund des Vorhandenseins der Metadaten [Date].

Validierung

Ein ungültiges Datum wird auf der Client-Seite markiert [1], wodurch verhindert wird, dass das Formular an den Server gesendet wird.

Das Fehlen eines Datums wird auf der Client-Seite nicht markiert, wohl aber auf der Server-Seite [2].

5.8.8. Eigenschaft [Time]

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [time] generiert. Dieser Wert wurde auf Grundlage der Metadaten generiert:


[DataType(DataType.Time)]

Der Typ [time] für ein <input>-Tag ist eine neue Funktion in HTML5. Der Chrome-Browser erkennt ihn und ermöglicht die Eingabe einer Uhrzeit im Format [hh:mm];

  • Die Methode [Html.DisplayFor] zeigte die Zeit ebenfalls im Format [hh:mm] an, wiederum aufgrund des Vorhandenseins der Metadaten [Time].

Validierung

Es ist technisch unmöglich, eine ungültige Zeit einzugeben. Das Fehlen einer Zeit wird serverseitig gemeldet:

Image

5.8.9. [HiddenInput]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut generiert, das auf [hidden] gesetzt ist, d. h. ein verstecktes Feld (das jedoch weiterhin übermittelt wird). Dieser Wert wurde aufgrund der Metadaten generiert:


[UIHint("HiddenInput")]

  • Die Methode [Html.DisplayFor] hat den Wert des versteckten Feldes geschrieben.

5.8.10. [Boolean]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut generiert, das auf [checkbox] gesetzt ist, d. h. ein Kontrollkästchen. Dieser Wert wurde generiert, da die Eigenschaft vom Typ Boolean ist:


public bool Boolean { get; set; }

  • Die Methode [Html.DisplayFor] hat Zeile 4 generiert, ebenfalls ein Kontrollkästchen (Attribut „type“), das jedoch deaktiviert ist (Attribut „disabled“).

5.8.11. [Email]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [email] generiert. Dieser Typ ist neu in HTML5. Dieser Typ wurde aufgrund der Metadaten generiert:


[DataType(DataType.EmailAddress)]

Chrome scheint diesen Typ als [text]-Typ behandelt zu haben.

  • Die Methode [Html.DisplayFor] hat Zeile 4 generiert: einen Link zur E-Mail-Adresse.

Validierung

Auf der Client-Seite wird eine ungültige Adresse gemeldet [1]:

Das Feld leer zu lassen, führt nicht zu einem Fehler.

5.8.12. [Url]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [url] generiert. Dieser Typ ist neu in HTML5. Er wurde aufgrund der Metadaten generiert:


[DataType(DataType.Url)]

Chrome scheint diesen Typ als [text]-Typ zu behandeln.

  • Die Methode [Html.DisplayFor] hat Zeile 4 generiert: einen Link zur URL.

Validierung

Auf der Client-Seite wird eine ungültige URL gemeldet [1]:

Keine Eingabe führt nicht zu einem Fehler.

5.8.13. Eigenschaft [Passwort]

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [password] generiert. Dieser Typ wurde auf Grundlage der Metadaten generiert:


[DataType(DataType.Password)]

  • Die Methode [Html.DisplayFor] hat Zeile 4 generiert.

5.8.14. [Currency]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> generiert, wobei das Attribut [type] auf [text] gesetzt wurde;

  • Die Methode [Html.DisplayFor] hat Zeile 4 generiert, eine Zahl mit zwei Dezimalstellen und einem Währungssymbol. Dieses Format wurde aufgrund der Metadaten verwendet:


[DataType(DataType.Currency)]

Validierung

Ein ungültiger Wert [1] oder ein fehlender Wert [2] wird auf der Serverseite gemeldet:

5.8.15. [CreditCard]-Eigenschaft

Definition


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

Ansicht


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

Visuell

Image

Generierter HTML-Code


      <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>

Kommentare

  • Die Methode [Html.EditorFor] hat in Zeile 3 das Tag <input> mit einem [type]-Attribut vom Typ [text] generiert. Die Methode [Html.DisplayFor] hat Zeile 4 generiert. Es ist hier nicht klar, welchen Beitrag die Metadaten leisten:


[DataType(DataType.CreditCard)]

Validierung

Es wird weder auf der Client-Seite noch auf der Server-Seite eine Validierung durchgeführt.

5.9. Formularvalidierung

Wir haben uns bereits in Abschnitt 4.5 und den folgenden Abschnitten mit der Validierung des Modells einer Aktion befasst. Wir greifen dieses Thema im Zusammenhang mit einem Formular erneut auf:

  • Wie man den Benutzer über Eingabefehler informiert;
  • Validierungen sowohl auf der Client- als auch auf der Serverseite durchführen, um Fehler dem Benutzer schneller zu melden.

5.9.1. Serverseitige Validierung

Betrachten Sie das folgende Modell:


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;
    }
  }
}

Dieses Modell wird von der folgenden Ansicht [Action11Get.cshtml] angezeigt:


@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>

  • Zeile 12: verweist auf das Stylesheet [Site.css]. Standardmäßig enthält es Klassen, die zur Hervorhebung von Formularfehlern verwendet werden;
  • Zeilen 18–25: eine dreispaltige Tabelle:
    • Spalte 1 zeigt Text mithilfe der Methode [Html.LabelFor] an;
    • Spalte 2 zeigt die Eingabe mithilfe der Methode [Html.EditorFor] an;
    • Spalte 3 zeigt etwaige Eingabefehler mithilfe der Methode [Html.ValidationMessageFor] an;

Die Aktion [Action11Get] wird verwendet, um das Formular anzuzeigen:


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

Die Aktion [Action11Post] wird verwendet, um das Formular bei Eingabefehlern erneut anzuzeigen:


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

  • Zeile 3: Das Modell [ViewModel11] wird erstellt und anschließend mit den übermittelten Werten initialisiert. Dabei können Fehler auftreten. Jede ungültige Eigenschaft P des Modells ist mit einer Fehlermeldung verknüpft. Dies ist die Meldung, die von der Methode [Html.ValidationMessageFor] des Formulars zurückgegeben wird.

Hier ein Ausführungsbeispiel:

Image

Image

Hier ist ein weiteres Beispiel:

Image

Beachten Sie, dass beide Datumsangaben falsch sind (das aktuelle Datum ist der 11.10.2013), die Fehler jedoch nicht gemeldet werden. Diese Fehler werden von der [Validate]-Methode des Modells erkannt:


    // 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;
}

Die Methode [Validate] wird erst ausgeführt, wenn alle Attributvalidierungen erfolgreich waren. Dies wird in einem abschließenden Beispiel veranschaulicht:

Image

5.9.2. Clientseitige Validierung

Alle bisherigen Validierungen wurden serverseitig durchgeführt. Dies erfordert einen Hin- und Rückweg zwischen Client und Server, damit der Benutzer seine Fehler sehen kann. Die clientseitige Validierung nutzt JavaScript-Code, um den Benutzer so früh wie möglich und in jedem Fall vor der POST-Anfrage über Fehler zu informieren. Die POST-Anfrage kann erst erfolgen, wenn alle erkannten Fehler korrigiert wurden.

Wir verwenden das vorherige [ViewModel11]-Modell, zeigen es nun aber mithilfe der folgenden [Action12Get.cshtml]-Ansicht an:


@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>

Hinweis: Zeile 13 – Passen Sie die jQuery-Version an diejenige an, die Sie mit Ihrer Version von Visual Studio verwenden (siehe unten).

Für die clientseitige Validierung muss die folgende Zeile 3 in der [Web.config]-Datei der Anwendung vorhanden sein.


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

  • Zeilen 1–4: Der Abschnitt [appSettings] muss ein direktes untergeordnetes Element des Abschnitts [configuration] in der Datei [Web.config] sein;

Die Ansicht [Action12Get] ist bis auf die Zeilen 13–15 identisch mit der vorherigen Ansicht [Action11Get]. Diese Zeilen enthalten die JavaScript-Skripte, die für die clientseitige Validierung in der Ansicht erforderlich sind. Diese Skripte befinden sich im Ordner [Scripts] des Projekts:

Jedes Skript verfügt über eine normale Version [.js] und eine minimierte Version [min.js]. Die letztere Version ist kleiner, aber unlesbar. Sie wird in der Produktion verwendet. Die lesbare Version wird in der Entwicklung verwendet.

Die Ansicht [Action12Get.cshtml] wird durch die folgende Aktion [Action12Get] angezeigt:


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

Das übermittelte Formular wird von der folgenden [Action12Post]-Aktion verarbeitet:


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

Schauen wir uns anhand eines Beispiels an, wie das funktioniert:

Sobald Sie ein Zeichen in [1] eingeben, erscheint die Meldung in [2], da der erwartete Wert mindestens vier Zeichen lang sein muss. Die Validierung erfolgt also bei jedem neu eingegebenen Zeichen. Die Fehlermeldung verschwindet, nachdem das vierte Zeichen eingegeben wurde. Nachdem das erledigt ist, senden wir das Formular ab:

Die URL [3] zeigt uns, dass der [POST]-Vorgang nicht stattgefunden hat. Durch Klicken auf die Schaltfläche [Validate] wurden jedoch alle clientseitigen Validierungen ausgelöst, und es erschienen neue Fehlermeldungen.

Schauen wir uns zum Beispiel den für die erste Eingabe generierten HTML-Code an:


        <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>

  • Zeile 3 enthält:
    • die Fehlermeldung für den Fall, dass eine Eingabe fehlt [data-val-required],
    • die Fehlermeldung für den Fall, dass die Eingabe falsch ist [data-val-regex],
    • den regulären Ausdruck für die eingegebene Zeichenfolge [data-val-regex-pattern];
  • Zeile 4, weitere Attribute [data-x] zur Anzeige von Fehlermeldungen;

Die [data-x]-Attribute der generierten Tags werden von dem JavaScript verwendet, das wir in die Ansicht eingebettet haben. Fehlt dieses JavaScript, werden diese Attribute einfach ignoriert, und es findet keine clientseitige Validierung statt. Es funktioniert genauso wie im vorherigen Beispiel. Daher der Begriff [unobtrusive] für diese Technik.

Wir erstellen die folgenden zwei Ansichten, um die Verwaltung von Links innerhalb einer Ansicht zu veranschaulichen:

  • In [1] und [2] haben wir zwei Navigationslinks;
  • in [3] gibt es einen Aktionslink, der das Formular absendet. Er wird nicht zur Navigation verwendet.

Seite 1 wird durch die folgende [Action16Get.cshtml]-Ansicht generiert:


@{
  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>

  • Zeile 22: Informationen, die von der Aktion initialisiert werden, die die Ansicht rendert;
  • Zeilen 23–28: ein Formular;
  • Zeile 25: eine Beschriftung für das Feld [data];
  • Zeile 26: ein Eingabefeld mit dem Namen [data];
  • Zeile 27: ein [submit]-Link. Beim Anklicken wird die JavaScript-Funktion [postForm] ausgeführt (href-Attribut). Diese Funktion ist in den Zeilen 12–17 definiert;
  • Zeile 14: Es wird eine Referenz für das erste Formular im Dokument abgerufen, das in Zeile 23;
  • Zeile 16: Dieses Formular wird übermittelt. Letztendlich verhält es sich so, als wäre eine [submit]-Schaltfläche angeklickt worden. Das Formular wird an den in Zeile 23 angegebenen Controller und die dort angegebene Aktion übermittelt;
  • Zeile 30: ein Navigationslink. Der generierte HTML-Code lautet wie folgt:


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

Die verwendete Methode ist ActionLink(Text, Action, Controller).

Seite 2 wird durch die folgende Ansicht [Action17Get.cshtml] generiert:


@{
  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>

Die Aktionen, die diese Aufrufe generieren, sind folgende:


      // 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();
}

  • Zeile 6: Die Aktion [Action16Get] rendert die Ansicht [Action16Get.cshtml], d. h. Seite 1 des Beispiels. Diese Ansicht basiert auf dem [ViewBag] (Zeile 5);
  • Zeile 19: Die Aktion [Action17Get] generiert die Ansicht [Action17Get.cshtml], d. h. Seite 2 des Beispiels. Diese Ansicht basiert auf dem [ViewBag] (Zeile 21);
  • Zeile 11: Die Aktion [Action16Post] verarbeitet die POST-Anfrage aus dem Formular in der Ansicht [Action16Get.cshtml]. Sie empfängt den Parameter mit dem Namen [data]. Zur Erinnerung: Dies ist der Name des Eingabefelds im Formular;
  • Zeile 13: Informationen werden in den [ViewBag] abgelegt;
  • Zeile 14: Die Ansicht [Action16Get.cshtml] wird angezeigt.

Leser werden dazu ermutigt, dieses Beispiel zu testen.