Skip to content

7. Ajax-Umstellung einer Anwendung ASP.NET MVC

7.1. Die Rolle von AJAX in einer Webanwendung

Derzeit weisen die untersuchten Lernbeispiele folgende Architektur auf:

Um von einer Ansicht [Vue1] zu einer Ansicht [Vue2] zu wechseln, führt der Browser folgende Schritte aus:

  • eine Anfrage an die Webanwendung senden;
  • erhält die Ansicht [Vue2] und zeigt sie anstelle der Ansicht [Vue1] an.

Das ist das klassische Schema:

  • Anfrage des Browsers;
  • Erstellung einer Ansicht als Antwort an den Client durch den Webserver;
  • Anzeige dieser neuen Ansicht durch den Browser.

Es gibt noch eine weitere Art der Interaktion zwischen dem Browser und dem Webserver: AJAX (Asynchronous JavaScript and XML). Dabei handelt es sich um Interaktionen zwischen der vom Browser angezeigten Ansicht und dem Webserver. Der Browser tut weiterhin das, was er kann, nämlich eine Ansicht anzeigen, wird nun aber durch in der angezeigten Ansicht eingebettetes JavaScript gesteuert. Das Schema sieht wie folgt aus:

  • in [1] tritt ein Ereignis auf der im Browser angezeigten Seite ein (Klick auf eine Schaltfläche, Änderung eines Textes usw.). Dieses Ereignis wird von in die Seite eingebettetem JavaScript (JS) abgefangen;
  • in [2] führt der JavaScript-Code eine Anfrage HTTP durch, wie es der Browser getan hätte. Die Anfrage erfolgt asynchron: Der Benutzer kann weiterhin mit der Seite interagieren, ohne durch das Warten auf die Antwort auf die Anfrage HTTP blockiert zu werden. Die Anfrage durchläuft den üblichen Verarbeitungsprozess. Sie unterscheidet sich in keiner (oder kaum) Weise von einer herkömmlichen Anfrage;
  • in [3] wird eine Antwort an den Client JS gesendet. Anstelle einer vollständigen Ansicht HTML wird eher eine Teilansicht HTML, ein Datenstrom XML oder JSON (JavaScript Object Notation), der gesendet wird;
  • In [4] ruft das JavaScript diese Antwort ab und verwendet sie, um einen Bereich der angezeigten Seite HTML zu aktualisieren.

Für den Benutzer ergibt sich eine Änderung der Ansicht, da sich das, was er sieht, geändert hat. Es findet jedoch kein vollständiges Neuladen der Seite statt, sondern lediglich eine teilweise Änderung der angezeigten Seite. Dies trägt zur Flüssigkeit und Interaktivität der Seite bei: Da kein vollständiges Neuladen der Seite erfolgt, können Ereignisse verarbeitet werden, die zuvor nicht berücksichtigt wurden. Beispielsweise kann dem Benutzer eine Liste mit Optionen angeboten werden, während er Zeichen in ein Eingabefeld eingibt. Bei jedem neu eingegebenen Zeichen wird eine Anfrage an den Server gesendet, der daraufhin weitere Vorschläge zurücksendet. Ohne Ajax war diese Art der Eingabehilfe früher unmöglich. Man konnte nicht bei jedem eingegebenen Zeichen eine neue Seite neu laden.

7.2. Grundlagen von JQuery und JavaScript

Wir haben die JavaScript-Bibliothek JQuery häufig in unsere Seiten eingebunden. Dort findet sich folgende Zeile:


  <script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>

Hinweis: Passen Sie die Version von jQuery an die Ihrer Visual Studio-Version an.

Die Ajax-Technologie von ASP.NET MVC nutzt JQuery. Wir werden selbst einige JQuery-Skripte schreiben. Daher stellen wir nun die Grundlagen von JQuery vor, die Sie kennen sollten, um die Skripte in diesem Kapitel zu verstehen.

Wir erstellen ein neues Projekt [Exemple-04] innerhalb unserer Lösung [Exemples]:

Um Ajax mit ASP.NET und MVC nutzen zu können, muss in der Konfigurationsdatei [Web.config] und [1] folgende Zeile vorhanden sein:


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

Zeile 3 ermöglicht die Verwendung von Ajax in den Ansichten ASP.NET. Sie ist standardmäßig vorhanden.

Wir erstellen eine Datei HTML [JQuery-01.html] im Ordner [Content] des neuen Projekts [2]:

Diese Datei hat folgenden Inhalt:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>JQuery-01</title>
  <script type="text/javascript" src="/Scripts/jquery-1.8.2.min.js"></script>
</head>
<body>
  <h3>Rudiments de JQuery</h3>
  <div id="element1">
    Elément 1
  </div>
</body>
</html>
  • Zeile 6: Import von JQuery (passen Sie die Versionsnummer an Ihre Visual-Studio-Version an);
  • Zeilen 10–12: ein Seitenelement mit der ID [element1]. Wir werden mit diesem Element experimentieren.

Wir zeigen diese Datei im Browser Google Chrome an: [4] und [5]:

Rufen Sie in Google Chrome die Tastenkombination [Ctrl-Maj-I] auf, um die Entwicklertools [6] anzuzeigen. Über die Registerkarte [Console] [7] können Sie JavaScript-Code ausführen. Im Folgenden geben wir JavaScript-Befehle an, die eingegeben werden müssen, und erläutern diese.

JS
Ergebnis
$("#element1")
: Gibt die Sammlung aller Elemente mit der ID [element1] zurück, also normalerweise eine Sammlung mit 0 oder 1 Element, da auf einer Seite keine zwei identischen IDs vorkommen können (HTML).
$("#element1").text("blabla")
: Wendet den Text „[blabla]“ auf alle Elemente der Sammlung an. Dadurch ändert sich der auf der Seite angezeigte Inhalt
$("#element1").hide()
blendet die Elemente der Sammlung aus. Der Text [blabla] wird nicht mehr angezeigt.
$("#element1")
: Zeigt die Sammlung wieder an. So können wir sehen, dass das Element mit der ID [element1] das Attribut CSS style='display: none;' besitzt, wodurch das Element ausgeblendet wird.
$("#element1").show()
: Zeigt die Elemente der Sammlung an. Der Text [blabla] erscheint wieder. Das Attribut CSS style='display: block;' sorgt für diese Anzeige.
$("#element1").attr('style','color: red')
: Weist allen Elementen der Sammlung ein Attribut zu. Das Attribut lautet hier [style] und sein Wert [color: red]. Der Text [blabla] wird rot dargestellt.
Tableau
Dictionnaire

Es ist zu beachten, dass sich der Wert „URL“ des Browsers während all dieser Vorgänge nicht verändert hat. Es fand kein Datenaustausch mit dem Webserver statt. Alles geschieht innerhalb des Browsers. Sehen wir uns nun den Quellcode der Seite an:

 

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>JQuery-01</title>
  <script type="text/javascript" src="/Scripts/jquery-1.8.2.min.js"></script>
</head>
<body>
  <h3>Rudiments de JQuery</h3>
  <div id="element1">
    Elément 1
  </div>
</body>
</html>

Das ist der ursprüngliche Text. Er spiegelt in keiner Weise die Änderungen wider, die wir an dem Element in den Zeilen 10–12 vorgenommen haben. Das sollte man beim Debuggen von JavaScript unbedingt beachten. Oft ist es daher sinnlos, den Quellcode der angezeigten Seite einzusehen. Um den Quellcode der aktuell angezeigten Seite zu ermitteln, gehen wir wie folgt vor:

 

Wir wissen nun genug, um die folgenden Skripte JS zu verstehen.

7.3. Aktualisierung einer Seite mit einem Feed HTML

7.3.1. Die Ansichten

Wir wollen die folgende Anwendung untersuchen:

  • in [1] den Zeitpunkt des Ladens der Seite;
  • in [2] werden die vier arithmetischen Operationen mit zwei reellen Zahlen A und B durchgeführt;
  • in [3] wird die Antwort des Servers in einen Bereich der Seite geschrieben;
  • in [4] ist der Zeitpunkt der Berechnung. Dieser unterscheidet sich vom Zeitpunkt des Ladens der Seite [5]. Letztere entspricht [1], was zeigt, dass der Bereich [6] nicht neu geladen wurde. Außerdem hat sich der Bereich URL [7] der Seite nicht verändert.

7.3.2. Der Controller, die Aktionen, das Modell, die Ansicht

Wir erstellen einen Controller mit dem Namen [Premier]:

Um die Startansicht anzuzeigen, erstellen wir die folgende Aktion „[Action01Get]“:


    [HttpGet]
    public ViewResult Action01Get()
    {
      ViewModel01 modèle = new ViewModel01();
      modèle.HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      return View(modèle);
}
  • Zeile 4: Instanziierung des Modells der Ansicht;
  • Zeile 5: Initialisierung des Ladezeitpunkts der Ansicht;
  • Zeile 6: Anzeige der Ansicht [Action10Get.cshtml] und ihres Modells.

Das Modell [ViewModel01] „ “ lautet wie folgt:


using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Exemple_04.Models
{
  [Bind(Exclude = "AplusB, AmoinsB, AmultipliéparB, AdiviséparB, Erreur, HeureChargement, HeureCalcul")]
  public class ViewModel01
  {
    // Formular
    [Required(ErrorMessage="Donnée requise")]
    [Display(Name="Valeur de A")]
    [Range(0, Double.MaxValue, ErrorMessage = "Tapez un nombre positif ou nul")]
    public double A { get; set; }
    [Required(ErrorMessage = "Donnée requise")]
    [Display(Name = "Valeur de B")]
    [Range(0, Double.MaxValue, ErrorMessage="Tapez un nombre positif ou nul")]
    public double B { get; set; }

    // Ergebnisse
    public string AplusB { get; set; }
    public string AmoinsB { get; set; }
    public string AmultipliéparB { get; set; }
    public string AdiviséparB { get; set; }
    public string Erreur { get; set; }
    public string HeureChargement { get; set; }
    public string HeureCalcul { get; set; }
  }
}
  • Zeilen 11–14: der Wert A des Formulars;
  • Zeilen 15–18: der Wert B des Formulars;
  • Zeilen 21–24: die Ergebnisse der vier arithmetischen Operationen mit A und B;
  • Zeile 25: der Text einer eventuellen Fehlermeldung;
  • Zeile 26: die Uhrzeit, zu der die Ansicht im Browser geladen wurde;
  • Zeile 27: der Zeitpunkt der Berechnung der Felder in den Zeilen 21–24;
  • Zeile 7: Diese Ansichtsvorlage ist gleichzeitig eine Aktionsvorlage. Aus dieser werden die Felder ausgeschlossen, die nicht vom Browser übermittelt werden.

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


@model Exemple_04.Models.ViewModel01
@{
  Layout = null;
  AjaxOptions ajaxOpts = new AjaxOptions
  {
    UpdateTargetId = "résultats",
    HttpMethod = "post",
    Url = Url.Action("Action01Post"),
    LoadingElementId = "loading",
    LoadingElementDuration = 1000
  };    
}

<!DOCTYPE html>

<html lang="fr-FR">
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Ajax-01</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>
  <script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
  <script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
  <script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.en-US.js"></script>
  <script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
  <script type="text/javascript" src="~/Scripts/myScripts-01.js"></script>
</head>
<body>

  <h2>Ajax - 01</h2>
  <p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
  <h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
  @using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
  {
    <table>
      <thead>
        <tr>
          <th>@Html.LabelFor(m => m.A)</th>
          <th>@Html.LabelFor(m => m.B)</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>@Html.TextBoxFor(m => m.A)</td>
          <td>@Html.TextBoxFor(m => m.B)</td>
        </tr>
        <tr>
          <td>@Html.ValidationMessageFor(m => m.A)</td>
          <td>@Html.ValidationMessageFor(m => m.B)</td>
        </tr>
      </tbody>
    </table>
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
  }
  <hr />
  <div id="résultats" />
</body>
</html>
  • Zeile 1: Die Ansicht basiert auf dem Typ [ViewModel01];
  • Zeile 21: JQuery wird sowohl für die Validierungen als auch für Ajax benötigt;
  • Zeilen 22–23: die Validierungsbibliotheken;
  • Zeilen 24–26: die Internationalisierungsbibliotheken;
  • Zeile 27: die Ajax-Bibliothek;
  • Zeile 28: eine lokale JavaScript-Bibliothek;
  • Zeile 33: Anzeige der Ladezeit der Ansicht;
  • Zeile 35: ein Ajax-Formular – darauf kommen wir noch zurück;
  • Zeilen 40–41: Beschriftungen für die Eingabefelder der Zahlen A und B;
  • Zeilen 46–47: Eingabefelder für die Zahlen A und B;
  • Zeilen 50–51: Fehlermeldungen für die Eingaben der Zahlen A und B;
  • Zeile 56: die Schaltfläche, die das Formular absendet. Dieses wird mit einer Ajax-Anfrage gesendet;
  • Zeile 57: Ein Ladebild, das während der Ajax-Anfrage angezeigt wird;
  • Zeile 58: Ein Link zum Absenden des Formulars über eine Ajax-Anfrage;
  • Zeile 62: ein <div>-Tag mit der ID [résultats]. Hier fügen wir den vom Webserver zurückgegebenen Feed HTML ein.

Diese Ansicht zeigt die folgende Seite an:

 

Sehen wir uns nun den Code an, der das Formular per Ajax überträgt:


...
@{
  Layout = null;
  AjaxOptions ajaxOpts = new AjaxOptions
  {
    UpdateTargetId = "résultats",
    HttpMethod = "post",
    Url = Url.Action("Action01Post"),
    LoadingElementId = "loading",
    LoadingElementDuration = 1000
  };    
}

...
  @using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
  {
....
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
}
...
 <div id="résultats" />
  • Zeile 15: Anstelle von [@Html.BeginForm] wird [@Ajax.BeginForm] verwendet. Diese Methode lässt zahlreiche Überladungen zu. Die verwendete Methode hat folgende Signatur:
Ajax.BeginForm(string ActionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string,object> htmlAttributes)

Wir verwenden hier die folgenden tatsächlichen Parameter:

Action01Post: Der Name der Aktion, die das POST des Formulars verarbeitet,

null: Es sind keine Routeninformationen anzugeben,

ajaxOpts: die Optionen des Ajax-Aufrufs. Diese wurden in den Zeilen 6–10 definiert,

new { id = "formulaire" }: um dem generierten <form>-Tag das Attribut [id='formulaire'] zuzuweisen;

Die verwendeten Ajax-Optionen lauten wie folgt:

  • Zeile 8: das Ziel der Ajax-Anfrage;
  • Zeile 7: Methode der Ajax-Anfrage HTTP;
  • Zeile 6: ID des Seitenbereichs, der durch die Antwort auf die Ajax-Anfrage aktualisiert wird;
  • Zeile 9: ID des Seitenbereichs, der während der Ajax-Anfrage angezeigt wird – in der Regel ein Ladebild. Hier wird Zeile 20 angezeigt. Sie enthält ein animiertes Bild, das das Warten symbolisiert. Zu Beginn wird dieses Bild durch den Stil [display : none] ausgeblendet;
  • Zeile 10: Wartezeit in Millisekunden, bevor das animierte Bild angezeigt wird, hier 1 Sekunde.

Der vom Ajax-Formular generierte Code HTML lautet wie folgt:


<form action="/Premier/Action01Post" data-ajax="true" data-ajax-loading="#loading" data-ajax-loading-duration="1000" data-ajax-method="post" data-ajax-mode="replace" data-ajax-update="#résultats" data-ajax-url="/Premier/Action01Post" id="formulaire" method="post">    <table>
...
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
</form>
<hr />
<div id="résultats" />
  • Zeile 1: das generierte <form>-Tag. Zu beachten sind die Attribute [data-ajax-attr], die die Werte der Felder des Objekts vom Typ [AjaxOptions] widerspiegeln, das der Ajax-Anfrage zugeordnet wurde. Diese Attribute werden von der Ajax-Bibliothek verwaltet. Ohne sie lautet das <form>-Tag:

<form action="/Premier/Action01Post" id="formulaire" method="post">
...
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
</form>

Es handelt sich dann um ein klassisches Formular vom Typ HTML. Dieser Code wird ausgeführt, wenn der Benutzer JavaScript in seinem Browser deaktiviert. Die Zeilen 5–6 bleiben dann ungenutzt.

7.3.3. Die Aktion [Action01Post]

Die Aktion [Action01Post], die die Ajax-Anfrage HTTP verarbeitet, lautet wie folgt:


    [HttpPost]
    public PartialViewResult Action01Post(FormCollection postedData, SessionModel session)
    {
      // Wartesimulation
      Thread.Sleep(2000);
      // Instanzierung des Aktionsmodells
      ViewModel01 modèle = new ViewModel01();
      // Berechnungszeit
      modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // Aktualisierung des Modells
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // Es wird ein Fehler zurückgegeben
        modèle.Erreur = getErrorMessagesFor(ModelState);
        return PartialView("Action01Error", modèle);
      }
      // In jedem zweiten Fall wird ein Fehler simuliert
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        modèle.Erreur = "[erreur aléatoire]";
        return PartialView("Action01Error", modèle);
      }
      // Berechnungen
      modèle.AplusB = string.Format("{0}", modèle.A + modèle.B);
      modèle.AmoinsB = string.Format("{0}", modèle.A - modèle.B);
      modèle.AmultipliéparB = string.Format("{0}", modèle.A * modèle.B);
      modèle.AdiviséparB = string.Format("{0}", modèle.A / modèle.B);
      // Ansicht
      return PartialView("Action01Success", modèle);
}
  • Zeile 1: Die Aktion verarbeitet nur eine [POST];
  • Zeile 2: Als Aktionsvorlage wird zugelassen:
    • [FormCollection postedData]: die Gesamtheit der von der Ajax-Anfrage POST übermittelten Werte,
    • [SessionModel session]: die Elemente der Sitzung. Hier wird eine in Abschnitt 4.10 beschriebene Technik verwendet;
  • Zeile 2: Die Aktion gibt ein Fragment HTML zurück und keine vollständige Seite HTML;
  • Zeile 5: Künstlich wird eine Pause von zwei Sekunden eingelegt, um eine lang andauernde Ajax-Aktion zu simulieren;
  • Zeile 7: Ein Modell vom Typ [ViewModel01] wird instanziiert;
  • Zeile 9: Die Berechnungszeit wird initialisiert;
  • Zeile 11: Es wird versucht, das Modell vom Typ [ViewModel01] mit den übermittelten Werten zu aktualisieren. Zur Erinnerung: Es gibt zwei Werte: die Werte der Zahlen A und B;
  • Zeile 12: Es wird überprüft, ob diese Aktualisierung erfolgreich war oder nicht;
  • Zeile 15: Im Fehlerfall wird das Feld [Erreur] des Modells gefüllt;
  • Zeile 16: Es wird eine Teilansicht [Action01Error.cshtml] zurückgegeben, die das Modell [ViewModel01] nutzt;
  • Zeilen 19–24: In jedem zweiten Fall wird ein Fehler simuliert;
  • Zeile 19: Es wird eine Zufallsganzzahl im Intervall [0,1] generiert. Der Zahlengenerator wird aus der Sitzung übernommen;
  • Zeile 20: Ist der generierte Wert 0, wird ein Fehler simuliert;
  • Zeile 22: Die Fehlermeldung wird in die Vorlage eingefügt;
  • Zeile 23: Es wird eine Teilansicht [Action01Error.cshtml] zurückgegeben, die das Modell [ViewModel01] nutzt;
  • Zeilen 26–29: Die arithmetischen Berechnungen mit den Zahlen A und B werden durchgeführt und die Ergebnisse als Zeichenfolgen in das Modell eingefügt;
  • Zeile 31: Es wird eine Teilansicht [Action01Success.cshtml] zurückgegeben, die die Vorlage [ViewModel01] verwendet;

7.3.4. Die Ansicht [Action01Error]

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


@model Exemple_04.Models.ViewModel01
<h4>Résultats</h4>
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p style="color: red;">Une erreur s'est produite : @Model.Erreur</p>

Zur Erinnerung: Dieser Teil-Feed HTML wird als Antwort auf die Ajax-Anfrage HTTP vom Typ POST gesendet und auf der Seite im Bereich mit der ID [résultats] platziert. Alle diese Informationen stammen aus der Ajax-Konfiguration, die auf der Hauptseite [Action01Get.cshtml] verwendet wird:


@model Exemple_04.Models.ViewModel01
@{
  Layout = null;
  AjaxOptions ajaxOpts = new AjaxOptions
  {
    UpdateTargetId = "résultats",
    HttpMethod = "post",
    Url = Url.Action("Action01Post"),
    LoadingElementId = "loading",
    LoadingElementDuration = 1000
  };    
}

Hier ein Beispiel für eine Antwort mit Fehler:

 

7.3.5. Die Ansicht [Action01Success]

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


@model Exemple_04.Models.ViewModel01
<h4>Résultats</h4>
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A+B=@Model.AplusB</p>
<p>A-B=@Model.AmoinsB</p>
<p>A*B=@Model.AmultipliéparB</p>
<p>A/B=@Model.AdiviséparB</p>

Auch hier wird dieser teilweise Feed HTML als Antwort auf die Ajax-Anfrage HTTP vom Typ POST gesendet und auf der Seite im Bereich mit der ID [résultats] platziert:

 

7.3.6. G -Sitzungsverwaltung

Wir haben gesehen, dass [Action01Post] die Sitzung nutzt. Das Sitzungsmodell ist der folgende Typ [SessionModel]:


using System;
namespace Exemple_03.Models
{
  public class SessionModel
  {
    public Random Randomizer { get; set; }
  }
}

Die Sitzung wird in [Global.asax] initialisiert:


    // Sitzung
    protected void Session_Start()
    {
      SessionModel sessionModel=new SessionModel();
      sessionModel.Randomizer=new Random(DateTime.Now.Millisecond);
      Session["data"] = sessionModel;
}

Die Zuordnung der Sitzung zu einem Modell erfolgt in [Application_Start]:


    protected void Application_Start()
    {
...
      // Modellbinder
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
}

Die Klasse [SessionModelBinder] wurde beschrieben.

7.3.7. Verwaltung des Wartebildes


@model Exemple_04.Models.ViewModel01
@{
  Layout = null;
  AjaxOptions ajaxOpts = new AjaxOptions
  {
...
    LoadingElementId = "loading",
    LoadingElementDuration = 1000
  };    
}

...
<body>

...
  @using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
  {
...
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
  }
...

Wenn die Ajax-Anfrage startet, wird nach einer Sekunde der Bereich mit der ID [loading] in Zeile 7 angezeigt: [ligne 8]. Dieser Bereich ist das ursprünglich ausgeblendete Bild aus Zeile 21. Daraus ergibt sich folgende Benutzeroberfläche:

Betrachten wir den Link [Calculer] auf der Hauptseite [Action01Get.cshtml]:


<head>
  <meta name="viewport" content="width=device-width" />
  <title>Ajax-01</title>
  ...
  <script type="text/javascript" src="~/Scripts/myScripts-01.js"></script>
</head>
<body>

  <h2>Ajax - 01</h2>
  <p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
  <h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
  @using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
  {
...
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
  }
  <hr />
<div id="résultats" />
  • Zeile 18: Ein Klick auf den Link [Calculer] löst die Ausführung der Funktion JS [postForm] aus. Diese ist in der Datei [myScripts-01.js] in Zeile 5 definiert. Das Skript lautet wie folgt:

function postForm() {
  // Man führt manuell einen Ajax-Aufruf mit JQuery durch
  var loading = $("#loading");
  var formulaire = $("#formulaire");
  var résultats = $('#Ergebnisse');
  $.ajax({
    url: '/Premier/Action01Post',
    type: 'POST',
    data: formulaire.serialize(),
    dataType: 'html',
    begin: loading.show(),
    success: function (data) {
      loading.hide()
      résultats.html(data);
    }
  })
}

// http://blog.instance-factory.com/?p=268
$.validator.methods.number = function (value, element) {
  return this.optional(element) ||
      !isNaN(Globalize.parseFloat(value));
}

$.validator.methods.date = function (value, element) {
  return this.optional(element) ||
      !isNaN(Globalize.parseDate(value));
}

jQuery.extend(jQuery.validator.methods, {
  range: function (value, element, param) {
    //Verwenden Sie das Globalisierungs-Plugin, um den Wert zu analysieren        
    var val = Globalize.parseFloat(value);
    return this.optional(element) || (
        val >= param[0] && val <= param[1]);
  }
});

Die Funktionen in den Zeilen 19–37 wurden bereits in Abschnitt 6.1 behandelt. Sie steuern die Internationalisierung der Seiten. Wir gehen nicht noch einmal darauf ein. In den Zeilen 1–17 führen wir den Ajax-Aufruf manuell durch, der im Fall der Schaltfläche [Calculer] zuvor über die dem Projekt zugeordnete Ajax-Bibliothek erfolgte. Dazu verwenden wir die dem Projekt zugeordnete Bibliothek JQuery.

  • Zeile 3: eine Referenz auf die Komponente mit der ID [loading]. [$("#loading")] liefert die Sammlung der Elemente mit der ID [loading] zurück. Es gibt nur eines;
  • Zeile 4: eine Referenz auf die Komponente mit der ID [formulaire];
  • Zeile 5: eine Referenz auf die Komponente mit der ID [résultats];
  • Zeile 6: der Ajax-Aufruf mit seinen Optionen;
  • Zeile 7: das Ziel des Ajax-Aufrufs URL;
  • Zeile 8: die verwendete Methode HTTP;
  • Zeile 9: die gesendeten Daten. [formulaire.serialize] erstellt die Zeichenkette [A=val1&B=val2] aus dem Feld POST des Formulars mit der ID [formulaire];
  • Zeile 10: Der erwartete Rückgabetyp. Es ist bekannt, dass der Server einen Datenstrom mit der ID HTML zurücksendet;
  • Zeile 11: Die Methode, die beim Start der Anfrage ausgeführt werden soll. Hier wird angegeben, dass die Komponente mit der ID [loading] angezeigt werden soll. Dabei handelt es sich um das animierte Ladebild;
  • Zeile 12: Die Methode, die bei erfolgreichem Abschluss der Ajax-Anfrage ausgeführt werden soll. Der Parameter [data] ist die vollständige Antwort des Servers. Wir wissen, dass es sich um einen Datenstrom HTML handelt;
  • Zeile 13: Das Wartezuegezeichen wird ausgeblendet;
  • Zeile 14: Die Komponente mit der ID [résultats] wird mit dem Wert HTML aus dem Parameter [data] aktualisiert.

Der Leser wird gebeten, den Link [Calculer] zu testen. Er funktioniert wie die Schaltfläche [Calculer], abgesehen von einer Fehler . Sobald dieser Link verwendet wurde, können ungültige Werte für A und B übermittelt werden:

  • Bei [1] und [2] wurden ungültige Werte eingegeben. Diese werden von den clientseitigen Validatoren gemeldet;
  • bei [3] wurde auf den Link [Calculer] geklickt;
  • In [4] gab es ein [POST], da man die Antwort [4] erhält.

Wenn die Werte ungültig sind und man auf die Schaltfläche [Calculer] klickt, findet die Übertragung von [POST] an den Server nicht statt. Im gleichen Fall findet bei dem Link „[Calculer]“ die Anfrage „[POST]“ an den Server statt. Es gibt also ein Verhalten der Schaltfläche „[Calculer]“, das wir mit dem Link „[Calculer]“ nicht reproduzieren konnten. Anstatt zu versuchen, dieses Problem jetzt zu lösen, heben wir es uns für ein späteres Beispiel auf, das auch ein weiteres Problem der clientseitigen Validierung veranschaulichen wird.

7.4. Aktualisierung einer Seite HTML mit einem Feed JSON

Im vorherigen Beispiel antwortete der Webserver auf die Ajax-Anfrage HTTP mit einem Datenstrom HTML. In diesem Datenstrom befanden sich Daten, die mit einer Formatierung HTML versehen waren. Wir möchten das vorherige Beispiel nun fortsetzen, diesmal jedoch mit Antworten vom Typ JSON (JavaScript Object Notation), die ausschließlich die Daten enthalten. Der Vorteil dabei ist, dass auf diese Weise weniger Bytes übertragen werden.

7.4.1. Die Aktion [Action02Get]

Die Aktion [Action02Get] wird der Einstiegspunkt der neuen Anwendung sein. Ihr Code lautet wie folgt:


@model Exemple_04.Models.ViewModel02
@{
  Layout = null;
  AjaxOptions ajaxOpts = new AjaxOptions
  {
    HttpMethod = "post",
    Url = Url.Action("Action02Post"),
    LoadingElementId = "loading",
    LoadingElementDuration = 1000,
    OnBegin = "OnBegin",
    OnFailure = "OnFailure",
    OnSuccess = "OnSuccess",
    OnComplete = "OnComplete"
  };    
}

<!DOCTYPE html>

<html lang="fr-FR">
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Ajax-02</title>
....
  <script type="text/javascript" src="~/Scripts/myScripts-02.js"></script>
</head>
<body>
  <h2>Ajax - 02</h2>
  <p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
  <h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
  @using (Ajax.BeginForm("Action02Post", null, ajaxOpts, new { id = "formulaire" }))
  {
...
    <p>
      <input type="submit" value="Calculer" />
      <img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
      <a href="javascript:postForm()">Calculer</a>
    </p>
  }
  <hr />
  <div id="entete">
    <h4>Résultats</h4>
    <p><strong>Heure de calcul : <span id="heureCalcul"/></strong></p>
  </div>
  <div id="résultats">
    <p>A+B=<span id="AplusB"/></p>
    <p>A-B=<span id="AmoinsB"/></p>
    <p>A*B=<span id="AmultipliéparB"/></p>
    <p>A/B=<span id="AdiviséparB"/></p>
  </div>
  <div id="erreur">
    <p style="color: red;">Une erreur s'est produite : <span id="msg"/></p>
  </div>
</body>
</html>
  • Zeilen 4–14: die Optionen des Ajax-Aufrufs;
  • Zeile 10: die Funktion JS, die beim Start der Anfrage ausgeführt werden soll. Diese Funktion ist in der Datei JS definiert, auf die in Zeile 24 verwiesen wird;
  • Zeile 11: die Funktion JS, die bei einem Fehlschlag der Anfrage ausgeführt werden soll;
  • Zeile 12: Die Funktion JS, die bei erfolgreichem Abschluss der Anfrage ausgeführt werden soll;
  • Zeile 13: die Funktion JS, die ausgeführt werden soll, nachdem die Ajax-Anfrage ihr Ergebnis erhalten hat (Fehlschlag oder Erfolg);
  • Zeilen 40–43: ein Bereich mit der ID [entete];
  • Zeilen 44–49: eine Region mit der ID [résultats]. Sie zeigt die Ergebnisse der vier arithmetischen Operationen an;
  • Zeilen 50–52: ein Bereich mit der ID [erreur]. Er zeigt gegebenenfalls eine Fehlermeldung an.

7.4.2. Die Aktion [Action02Post]

Die Ajax-Anfrage wird von der folgenden Aktion [Action02Post] verarbeitet:


[HttpPost]
    public JsonResult Action02Post(FormCollection postedData, SessionModel session)
    {
      // Simulation ausstehend
      Thread.Sleep(2000);
      // Modellvalidierung
      ViewModel02 modèle = new ViewModel02();
      // Lade- und Berechnungszeiten
      string HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      string HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // Modellaktualisierung
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // Es wird ein Fehler zurückgegeben
        return Json(new { Erreur = getErrorMessagesFor(ModelState), HeureCalcul = HeureCalcul });
      }
      // In jedem zweiten Fall wird ein Fehler simuliert
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        // wird ein Fehler zurückgegeben
        return Json(new { Erreur = "[erreur aléatoire]", HeureCalcul = HeureCalcul });
      }
      // Berechnungen
      string AplusB = string.Format("{0}", modèle.A + modèle.B);
      string AmoinsB = string.Format("{0}", modèle.A - modèle.B);
      string AmultipliéparB = string.Format("{0}", modèle.A * modèle.B);
      string AdiviséparB = string.Format("{0}", modèle.A / modèle.B);
      // die Ergebnisse werden zurückgegeben
      return Json(new { Erreur = "", AplusB = AplusB, AmoinsB = AmoinsB, AmultipliéparB = AmultipliéparB, AdiviséparB = AdiviséparB, HeureCalcul = HeureCalcul });
    }
  • Zeile 2: Die Methode gibt einen Typ [JsonResult] zurück, d. h. einen Text im Format JSON;
  • Zeile 16: Die Informationen werden in Form einer anonymen, serialisierten Klasseninstanz in JSON zurückgegeben. Die Methode [getErrorMessagesFor] wurde bereits vorgestellt. Die an den Browser gesendete Zeichenfolge JSON hat folgende Form:
{"Erreur":"[erreur aléatoire]","HeureCalcul":"05:31:37"}
  • Zeile 31: Gleiches Vorgehen für die arithmetischen Ergebnisse. Diesmal hat die an den Browser gesendete Zeichenkette JSON folgende Form:
{"Erreur":"","AplusB":"4","AmoinsB":"-2","AmultipliéparB":"3","AdiviséparB":"0,333333333333333","HeureCalcul":"05:52:17"}

7.4.3. Der clientseitige JavaScript-Code

Zur Erinnerung: Die Konfiguration des Ajax-Aufrufs auf der Seite „HTML“, die an den Client-Browser gesendet wird:


  AjaxOptions ajaxOpts = new AjaxOptions
  {
    HttpMethod = "post",
    Url = Url.Action("Action02Post"),
    LoadingElementId = "loading",
    LoadingElementDuration = 1000,
    OnBegin = "OnBegin",
    OnFailure = "OnFailure",
    OnSuccess = "OnSuccess",
    OnComplete = "OnComplete"
};    

Die in den Zeilen 7–10 (rechts vom Gleichheitszeichen =) referenzierten Funktionen JS sind in der folgenden Datei [myScripts-02.js] definiert:


// Gesamtdaten
var entete;
var loading;
var résultats;
var erreur;
var heureCalcul;
var msg;
var AplusB;
var AmoinsB;
var AmultipliéparB;
var AdiviséparB;
var formulaire;
...
function postForm() {
...
}

// beim Laden des Dokuments
$(document).ready(function () {
  formulaire = $("#formulaire");
  entete = $("#entete");
  loading = $("#loading");
  erreur = $("#erreur");
  résultats = $('#Ergebnisse');
  heureCalcul = $("#heureCalcul");
  msg = $("#msg");
  AplusB = $("#AplusB");
  AmoinsB = $("#AmoinsB");
  AmultipliéparB = $("#AmultipliéparB");
  AdiviséparB = $("#AdiviséparB");

  // Bestimmte Elemente der Seite werden ausgeblendet
  entete.hide();
  résultats.hide();
  erreur.hide();
});

// Start
function OnBegin() {
....
}

// Ende der Anfrage
function OnComplete() {
...
}

// erfolgreich
function OnSuccess(data) {
....
}

// Fehler
function OnFailure(request, error) {
...
}
  • Zeile 19: Die Funktion JS wird am Ende des Ladevorgangs der Seite im Browser ausgeführt;
  • Zeilen 20–30: Es werden die Referenzen aller Komponenten der Seite abgerufen, die für uns von Interesse sind. Die Suche nach einer Komponente auf einer Seite ist mit einem gewissen Aufwand verbunden, daher ist es ratsam, sie nur einmal durchzuführen;
  • Zeilen 33–35: Die Komponenten [entete], [résultats] und [loading] werden ausgeblendet;

Beim Start der Ajax-Anfrage wird die folgende Funktion ausgeführt:


// Start
function OnBegin() {
  // Warteanzeige leuchtet
  loading.show();
  // Bestimmte Elemente der Seite werden ausgeblendet
  entete.hide();
  résultats.hide();
  erreur.hide();
}
  • Zeile 4: Die Komponente [loading] wird angezeigt. Dabei handelt es sich um das animierte Bild;
  • Zeilen 6–8: Die Komponenten [entete], [résultats] und [erreur] werden ausgeblendet;

Wenn die Ajax-Anfrage erfolgreich ist, wird der folgende Code JS ausgeführt:


// erfolgreich
function OnSuccess(data) {
  // Ergebnisse werden angezeigt
  heureCalcul.text(data.HeureCalcul);
  entete.show();
  if (data.Erreur != '') {
    msg.text(data.Erreur);
    erreur.show();
    return;
  }
  // kein Fehler
  AplusB.text(data.AplusB);
  AmoinsB.text(data.AmoinsB);
  AmultipliéparB.text(data.AmultipliéparB);
  AdiviséparB.text(data.AdiviséparB);
  résultats.show();
}

Um diesen Code zu verstehen, muss man sich an die beiden Texte JSON erinnern, die als Antwort an den Browser gesendet werden können:

{"Erreur":"[erreur aléatoire]","HeureCalcul":"05:31:37"}

im Fehlerfall, andernfalls die Zeichenfolge:

{"Erreur":"","AplusB":"4","AmoinsB":"-2","AmultipliéparB":"3","AdiviséparB":"0,333333333333333","HeureCalcul":"05:52:17"}

Bezeichnet man diese Zeichenfolge als [data], so erhält man den Wert des Feldes [Erreur] entweder durch die Schreibweise [data.Erreur] oder [data["Erreur"]], je nach Wahl. Das Gleiche gilt für die anderen Felder der Zeichenfolge JSON. Um einer Komponente mit der ID X einen unformatierten Text zuzuweisen, schreibt man übrigens [X.text(chaine)]. Kehren wir zum Code der Funktion [OnSuccess] zurück:

  • Zeile 2: [data] ist die empfangene Zeichenfolge JSON;
  • Zeile 4: Die Komponente [heureCalcul] erhält ihren Wert;
  • Zeile 5: Die Komponente [entete] wird angezeigt;
  • Zeile 6: Prüfung des Feldes [Erreur] der Zeichenfolge JSON;
  • Zeile 7: Die Komponente [msg] erhält ihren Wert;
  • Zeile 8: Die Komponente [erreur] wird angezeigt;
  • Zeile 9: Im Fehlerfall ist der Vorgang beendet;
  • Zeile 12: Die Komponente [AplusB] erhält ihren Wert;
  • Zeile 13: Die Komponente [AmoinsB] erhält ihren Wert;
  • Zeile 14: Die Komponente [AmultipliéparB] erhält ihren Wert;
  • Zeile 15: Die Komponente [AdiviséparB] erhält ihren Wert;
  • Zeile 16: Die Komponente [résultats] wird angezeigt.

Die Funktion [OnFailure] „ “ wird ausgeführt, falls die Ajax-Anfrage HTTP fehlschlägt. Dieser Fehler wird anhand des vom Server zurückgegebenen Codes HTTP gemessen. Der Code 500 [Internal Server Error] weist beispielsweise darauf hin, dass der Server die Anfrage nicht ausführen konnte. Die Funktion [OnFailure] lautet wie folgt:


// Fehler
function OnFailure(request, error) {
  alert("L'erreur suivante s'est produite :" + error);
}

Es wird lediglich ein Dialogfeld mit dem aufgetretenen Fehler angezeigt. In der Praxis wäre eine genauere Angabe erforderlich. Wir werden in Kürze eine andere Lösung vorschlagen.

Schließlich wird die Funktion [OnComplete] ausgeführt, sobald die Anfrage abgeschlossen ist, unabhängig davon, ob sie erfolgreich war oder fehlgeschlagen ist.


// Ende der Anfrage
function OnComplete() {
  // Wartezeichen ausgeschaltet
  loading.hide();
}

An dieser Stelle sei daran erinnert, dass die Konfiguration des Ajax-Aufrufs in der Ansicht [Action02Get.cshtml] dafür sorgt, dass diese verschiedenen Funktionen aufgerufen werden:


  AjaxOptions ajaxOpts = new AjaxOptions
  {
...
    OnBegin = "OnBegin",
    OnFailure = "OnFailure",
    OnSuccess = "OnSuccess",
    OnComplete = "OnComplete"
};

Der Code HTML des Links [Calculer] in der Ansicht [Action02Get.cshtml] lautet wie folgt:


      <a href="javascript:postForm()">Calculer</a>

Die Funktion JS [postForm] befindet sich in der importierten Datei [myScripts-02.js]:


  <script type="text/javascript" src="~/Scripts/myScripts-02.js"></script>

Der Code lautet wie folgt:


function postForm() {
  // Manueller Ajax-Aufruf mit JQuery
  $.ajax({
    url: '/Premier/Action02Post',
    type: 'POST',
    data: formulaire.serialize(),
    dataType: 'json',
    beforeSend: OnBegin,
    success: OnSuccess,
    error: OnFailure,
    complete: OnComplete
  })
}

Ein ähnlicher Code ist uns bereits begegnet.

  • Zeile 4: URL als Ziel des Ajax-Aufrufs;
  • Zeile 5: Befehl HTTP, der vom Ajax-Aufruf verwendet wird;
  • Zeile 6: Übermittelte Werte. Sie sind das Ergebnis der Serialisierung der Formularwerte. Das Formular mit der ID [formulaire] wird durch die Variable [formulaire] referenziert. [data] ist eine Zeichenkette der Form [A=val1&B=val2];
  • Zeile 7: Formatierungstyp der erwarteten Antwort. Es handelt sich um eine Zeichenkette JSON;
  • Zeile 8: Die Funktion JS, die beim Start des Ajax-Aufrufs ausgeführt werden soll;
  • Zeile 9: Funktion „JS“, die ausgeführt werden soll, wenn der Ajax-Aufruf erfolgreich ist;
  • Zeile 10: Funktion JS, die ausgeführt werden soll, wenn der Ajax-Aufruf fehlschlägt;
  • Zeile 11: Funktion JS, die nach Erhalt der Antwort vom Server ausgeführt werden soll, unabhängig davon, ob diese erfolgreich ist oder einen Fehler enthält.

Kommen wir noch einmal auf die JavaScript-Funktion zurück, die den Fall behandelt, in dem der Ajax-Aufruf fehlschlägt (Zeile 10). Der Ajax-Aufruf schlägt in verschiedenen Situationen fehl, beispielsweise wenn der Server einen Fehlercode wie [403 Forbidden], [404 Not Found], [500 Internal Server Error], [301 Moved Permanently] usw. zurückgibt.

Im vorangegangenen Beispiel lautet die Funktion [OnFailure] wie folgt:


// Fehler
function OnFailure(request, error) {
  alert("L'erreur suivante s'est produite :" + error);
}

Im Allgemeinen liefert die Anzeige des Objekts [error] keine interessanten Informationen. Wenn ein Ajax-Aufruf mit JQuery verwendet wird, kann die folgende Methode [OnFailure] verwendet werden:


// Fehler
function OnFailure(jqXHR) {
  alert("Erreur : " + jqXHR.status + " " + jqXHR.statusText);
  msg.html(jqXHR.responseText);
  erreur.show();
}

Das Objekt JQuery [jqXHR] verfügt unter anderem über folgende Eigenschaften:

  • responseText: der Text der Serverantwort;
  • status: der vom Server zurückgegebene Fehlercode;
  • statusText: der diesem Fehlercode zugeordnete Text.
  • Zeile 3: Der Fehlercode und die dazugehörige Beschreibung werden angezeigt;
  • Zeile 4: Die Serverantwort HTML wird in die Komponente mit der ID [msg] eingefügt;
  • Zeile 5: Die Region mit der ID [erreur] wird angezeigt.

Um diese Fehlerfunktion zu testen, werden wir in der Aktion [Action02Post] künstlich eine Ausnahme auslösen:


    [HttpPost]
    public JsonResult Action02Post(FormCollection postedData, SessionModel session)
    {
      // Eine künstliche Ausnahme zum Testen der Fehlerbehandlung des Ajax-Aufrufs
      throw new Exception();
      // Wartezeit-Simulation
      Thread.Sleep(2000);
      // Validierung des Modells
...

Zeile 5 löst eine Ausnahme aus. Testen wir nun die Anwendung:

Wir erhalten die folgende Antwort: [1] und [2]:

Anhand der Serverantwort können wir erkennen, in welcher Zeile des Servercodes der Fehler aufgetreten ist. Diese Information ist oft nützlich. Von nun an werden wir diese Technik nutzen, um Fehler bei Ajax-Aufrufen zu behandeln.

7.5. Single-Page-Webanwendung

Die Ajax-Technologie ermöglicht die Erstellung von Single-Page-Anwendungen:

  • Die erste Seite wird über eine herkömmliche Browseranfrage geladen;
  • die folgenden Seiten werden über Ajax-Aufrufe abgerufen. Somit wechselt der Browser letztendlich nie die URL und lädt nie eine neue Seite. Diese Art von Anwendung wird als Single-Page-Anwendung (APU) oder auf Englisch als Single Page Application (SPA) bezeichnet.

Hier ist ein einfaches Beispiel für eine solche Anwendung. Die neue Anwendung wird zwei Ansichten haben:

  • In [1] ermöglicht uns die Aktion [Action03Get], die erste Seite, also Seite 1, aufzurufen;
  • in [2] ermöglicht uns ein Link, mittels eines Ajax-Aufrufs zu Seite 2 zu wechseln;
  • In [3] hat sich URL nicht geändert. Die angezeigte Seite ist Seite 2;
  • In [4] ermöglicht uns ein Link, mittels eines Ajax-Aufrufs zu Seite 1 zurückzukehren;
  • In [5] hat sich URL nicht geändert. Die angezeigte Seite ist Seite 1.

Der Code der Aktion [Action03Get] lautet wie folgt:


    [HttpGet]
    public ViewResult Action03Get()
    {
      return View();
}
  • Zeile 4: Die Ansicht [Action03Get.cshtml] wird angezeigt.

Die Ansicht [Action03Get.cshtml] lautet wie folgt:


@{
  Layout = null;
}

<!DOCTYPE html>

<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action03Get</title>
  <script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
  <script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
</head>
<body>
  <h3>Ajax - 03 - Single Page Application</h3>
  <div id="content">
    @Html.Partial("Page1")
  </div>
</body>
</html>
  • Zeilen 16–18: ein Element mit der ID [content]. In diesem Element werden die verschiedenen Seiten angezeigt;
  • Zeile 17: Standardmäßig wird zunächst die Seite [Page1.cshtml] angezeigt.

Die Seite [Page1.cshtml] ist die folgende:


<h4>Page 1</h4>
  <p>
    @Ajax.ActionLink("Page 2", "Action04", new { Page = 2 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>
  • Zeile 1: Der Titel der Seite, um sie von Seite 2 zu unterscheiden;
  • Zeile 3: ein Ajax-Link mit den folgenden Parametern:
    • der Linktext [Page 2];
    • die Zielaktion des Links [Action04];
    • die Parameter des angeforderten URL. Diese lauten [/Premier/Action04?Page=2];
  • die Optionen des Ajax-Aufrufs. Hier wird lediglich die ID des Bereichs angegeben, der mit der Serverantwort aktualisiert werden soll. Für die übrigen Optionen werden, sofern vorhanden, die Standardwerte verwendet. Die Standardmethode für HTTP ist GET.

Schauen wir uns an, was passiert, wenn der Link angeklickt wird. Die Aktion URL wird mit einem [/Premier/Action04?Page=2] angefordert. Daraufhin wird die Aktion [Action04] ausgeführt:


    [HttpGet]
    public PartialViewResult Action04(string page = "1")
    {
      string vue = "Page1";
      if (page == "2")
      {
        vue = "Page2";
      }
      return PartialView(vue);
}
  • Zeile 2: Die Aktion gibt einen Teil-HTML-Stream zurück;
  • Zeile 2: Die Aktion hat die Zeichenkette [page] als Vorlage. Nun ist bekannt, dass URL folgende Information enthält: [/Premier/Action04?Page=2]. Es sei daran erinnert, dass die Vorlage nicht zwischen Groß- und Kleinschreibung unterscheidet;
  • Zeilen 4–8: [vue] erhält den Wert [Page2];
  • Zeile 9: Die Teilansicht [Page2.cshtml] wird zurückgegeben.

Die Teilansicht [Page2.cshtml] lautet wie folgt:


<h4>Page 2</h4>
  <p>
    @Ajax.ActionLink("Page 1", "Action04", new { Page = 1 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>

Der Server sendet also den oben genannten Datenstrom HTML als Antwort auf den Ajax-Aufruf GET [/Premier/Action04?Page=2] zurück. Zur Erinnerung: Dieser Ajax-Aufruf verwendet diese Antwort, um den Bereich mit der ID [content] (Zeile 3 unten) zu aktualisieren:


<h4>Page 1</h4>
  <p>
    @Ajax.ActionLink("Page 2", "Action04", new { Page = 2 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>

Dies führt zur folgenden neuen Anzeige: [1]:

Nach dem gleichen Prinzip lässt sich erkennen, dass ein Klick auf den Link [Page 1] in [1] die Anzeige von [2] auslöst.

Kehren wir zum allgemeinen Schema einer Anwendung ASP.NET MVC zurück:

Dank des in die Seiten HTML eingebetteten und im Browser ausgeführten JavaScripts lässt sich Code auf den Browser verlagern, wodurch folgende Architektur entsteht:

  • In [1] ist die Webschicht ASP.NET MVC zu einer Webschnittstelle für den Zugriff auf Daten geworden, die in der Regel in einer Datenbank gespeichert sind. Die bereitgestellten Ansichten enthalten ausschließlich Daten und keinerlei Layout-Elemente, wie beispielsweise die Feeds HTML, XML oder JSON;
  • in [2]: Der Browser zeigt statische (d. h. nicht dynamisch generierte) Ansichten an, die von einem Webserver bereitgestellt werden, der sich auf demselben Rechner wie der Server [1] befinden kann oder auch nicht. Diese statischen Ansichten werden anschließend durch die Daten ergänzt, die das JavaScript über die Webschnittstelle [1] abruft;
  • Der in die Seiten HTML eingebettete JavaScript-Code kann in Schichten strukturiert sein:
    • Die Ebene [présentation] ist für die Interaktionen mit dem Benutzer zuständig,
    • die Ebene [DAO] ist für den Zugriff auf die Daten über den Webserver [1] zuständig,
    • die Schicht [métier] entspricht der Schicht [métier], die sich zuvor auf dem Server [1] befand und auf den Browser [2] verlagert wurde;

Der Vorteil dieser Architektur besteht darin, dass sie unterschiedliche Kompetenzen einbezieht:

  • Der Code des Webservers [1] erfordert Kenntnisse in .NET, jedoch keine Kenntnisse in JavaScript, HTML und CSS;
  • der im Browser eingebettete Code [2] erfordert Kenntnisse in JavaScript, HTML und CSS, ist jedoch unabhängig von der Technologie des Webservers [1].

Somit erleichtert diese Architektur die parallele Arbeit von Teams mit unterschiedlichen Kompetenzen. Sie eignet sich besonders gut für Single-Page-Anwendungen.

7.6. Single-Page-Webanwendung und clientseitige Validierung

Wir haben zuvor im Beispiel Ajax-01 eine Anomalie angesprochen. Hier noch einmal der Kontext:

  • In [1] und [2] wurden ungültige Werte eingegeben. Diese werden von den clientseitigen Validatoren gemeldet;
  • in [3] wurde auf den Link [Calculer] geklickt;
  • In [4] gab es ein [POST], da man die Antwort [4] erhält.

Wenn die Werte ungültig sind und man auf die Schaltfläche [Calculer] klickt, findet die Übertragung von [POST] an den Server nicht statt. Im gleichen Fall findet bei dem Link „[Calculer]“ die Anfrage „[POST]“ an den Server statt. Es gibt also ein Verhalten der Schaltfläche „[Calculer]“, das wir mit dem Link „[Calculer]“ nicht reproduzieren konnten.

Wir werden dieses Beispiel in einem neuen Kontext aufgreifen: Die Anwendung wird mehrere Ansichten haben und vom Typ [Application à Page Unique] sein, den wir gerade beschrieben haben.

7.6.1. Die Ansichten des Beispiels

Das Beispiel umfasst mehrere Ansichten:

  • in [1] die Ansicht [Action05Get];
  • in [2] die Teilansicht [Formulaire05];
  • in [3] die Teilansicht [Failure05];
  • in [4] die Teilansicht [Success05].

Die Anwendung besteht aus einer einzigen Seite: Diese wird beim ersten Aufruf vom Browser geladen. Anschließend wird sie durch Ajax-Aufrufe aktualisiert.

Die vorherigen Seiten werden von den folgenden Ansichten [cshtml] generiert:

Die ursprünglich geladene Ansicht ist die folgende Ansicht [Action05Get.cshtml]:


@model Exemple_04.Models.ViewModel05
@{
  Layout = null;
}

<!DOCTYPE html>

<html lang="fr-FR">
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Ajax-05</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>
  <script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
  <script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
  <script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.en-US.js"></script>
  <script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
  <script type="text/javascript" src="~/Scripts/myScripts-05.js"></script>
</head>
<body>

  <h2>Ajax - 05, Page unique - Validation formulaire côté client</h2>
  <p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
  <h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
  <img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
  <div id="content">
    @Html.Partial("Formulaire05", Model)
  </div>
</body>
</html>

Beachten Sie dabei folgende Punkte:

  • Zeile 1: Das Modell der Ansicht ist der Typ [ViewModel05], den wir in Kürze vorstellen werden;
  • Zeilen 13–19: Hier finden sich die erforderlichen JavaScript-Skripte für Ajax und die clientseitige Validierung;
  • Zeile 20: Wir werden unsere eigenen JavaScript-Funktionen in [myScripts-05.js] hinzufügen;
  • Zeile 27: das animierte Ladebild;
  • Zeilen 28–30: ein ID-Tag [content]. In dieses Tag werden die Teilansichten [Formulaire05, Success05, Failure05] eingefügt;
  • Zeile 29: Einfügen der Teilansicht [Formulaire05].

Die Teilansicht [Action05Get] ist für die Anzeige des Teils [1] der Startseite zuständig:

Die Teilansicht [Formulaire05] generiert den oben genannten Teil [2]. Ihr Code lautet wie folgt:


@model Exemple_04.Models.ViewModel05

@using (Html.BeginForm("Action05Post", "Premier", FormMethod.Post, new { id = "formulaire" }))
{
  <table>
    <thead>
      <tr>
        <th>@Html.LabelFor(m => m.A)</th>
        <th>@Html.LabelFor(m => m.B)</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>@Html.TextBoxFor(m => m.A)</td>
        <td>@Html.TextBoxFor(m => m.B)</td>
      </tr>
      <tr>
        <td>@Html.ValidationMessageFor(m => m.A)</td>
        <td>@Html.ValidationMessageFor(m => m.B)</td>
      </tr>
    </tbody>
  </table>
  <p>
    <table>
      <tbody>
        <tr>
          <td><a href="javascript:calculer()">Calculer</a>
          </td>
          <td style="width: 20px" />
          <td><a href="javascript:effacer()">Effacer</a>
          </td>
        </tr>
      </tbody>
    </table>
  </p>
}
  • Zeile 1: Die Teilansicht akzeptiert als Vorlage einen Typ [ViewModel05];
  • Zeile 3: Das durch die Methode [Html.BeginForm] generierte Formular. Da dieses Formular über einen Ajax-Aufruf gesendet wird, werden die ersten drei Parameter der Methode ignoriert. Es sei denn, der Benutzer hat JavaScript in seinem Browser deaktiviert. Diese Möglichkeit lassen wir hier außer Acht. Der vierte Parameter ist wichtig. Das Formular erhält die ID [formulaire];
  • Zeilen 5–22: das Formular zur Eingabe der Zahlen A und B;
  • Zeile 27: ein JavaScript-Link, der die Ausführung der vier arithmetischen Operationen mit A und B auslöst;
  • Zeile 30: ein JavaScript-Link, der die Eingaben und eventuelle damit verbundene Fehlermeldungen löscht.

Es ist zu beachten, dass das Formular keine Schaltfläche vom Typ [submit] enthält. Wir müssen die Berechnung der eingegebenen Werte A und B manuell durchführen.

Wenn keine Fehler vorliegen, werden die Ergebnisse angezeigt:

Der obige Teil „[4]“ wird durch die folgende Teilansicht „[Success05.cshtml]“ generiert:


@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h4>Résultats</h4>
<p>A+B=@Model.AplusB</p>
<p>A-B=@Model.AmoinsB</p>
<p>A*B=@Model.AmultipliéparB</p>
<p>A/B=@Model.AdiviséparB</p>
<p>
  <a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
  • Zeile 1: Die Teilansicht [Success05.cshtml] erhält eine Vorlage vom Typ [ViewModel05];
  • Zeile 12: Ein JavaScript-Link, um zu den Eingaben zurückzukehren.

Im Fehlerfall wird eine andere Teilansicht [3] angezeigt:

Diese Ansicht wird durch den folgenden Code [Failure05.cshtml] generiert:


@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
  @foreach (string msg in Model.Erreurs)
  {
    <li>@msg</li>
  }
</ul>
<p>
  <a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
  • Zeile 1: Die Teilansicht [Failure05.cshtml] erhält eine Vorlage vom Typ [ViewModel05];
  • Zeile 14: Ein JavaScript-Link, um zu den Eingaben zurückzukehren.

7.6.2. Die Vorlage der Ansichten

Alle oben genannten Ansichten nutzen dieselbe Vorlage [ViewModel05]:


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Exemple_04.Models
{
  [Bind(Exclude = "AplusB, AmoinsB, AmultipliéparB, AdiviséparB, Erreurs, HeureChargement, HeureCalcul")]
  public class ViewModel05
  {
    // Formular
    [Required(ErrorMessage="Donnée A requise")]
    [Display(Name="Valeur de A")]
    [Range(0, Double.MaxValue, ErrorMessage = "Tapez un nombre A positif ou nul")]
    public string A { get; set; }
    [Required(ErrorMessage = "Donnée B requise")]
    [Display(Name = "Valeur de B")]
    [Range(0, Double.MaxValue, ErrorMessage="Tapez un nombre B positif ou nul")]
    public string B { get; set; }

    // Ergebnisse
    public string AplusB { get; set; }
    public string AmoinsB { get; set; }
    public string AmultipliéparB { get; set; }
    public string AdiviséparB { get; set; }
    public List<string> Erreurs { get; set; }
    public string HeureChargement { get; set; }
    public string HeureCalcul { get; set; }
  }
}

Es handelt sich um das bereits vorgestellte Modell [ViewModel01], mit einigen kleinen Abweichungen:

  • Zeilen 15 und 19: Die Felder A und B haben nun den Typ [string], damit bei der ersten Anzeige des Eingabeformulars leere Eingabefelder statt Felder mit dem Wert 0 angezeigt werden;
  • Zeilen 14 und 18: Dies hindert nicht daran, den eingegebenen Wert mit einem Validator vom Typ [Range] zu überprüfen;
  • Zeile 26: Eine Liste mit Fehlermeldungen, die von der Ansicht [Failure05] angezeigt wird.

7.6.3. Die Daten des Bereichs [Session]

In Abschnitt 7.3.6 haben wir gesehen, dass die Daten der Sitzung in das folgende Modell [SessionModel] gekapselt waren:


using System;
namespace Exemple_03.Models
{
  public class SessionModel
  {
    // Der Zufallszahlengenerator
    public Random Randomizer { get; set; }
  }
}

Dieses Sitzungsmodell wird erweitert, um die Werte von A und B einzubeziehen:


using System;
namespace Exemple_03.Models
{
  public class SessionModel
  {
    // der Zufallszahlengenerator
    public Random Randomizer { get; set; }
    // die Werte von A und B
    public string A { get; set; }
    public string B { get; set; }
  }
}

Es ist in der Tat notwendig, die Werte von A und B in der Sitzung zu speichern, wie die folgende Sequenz zeigt:

Anfrage 1

Anfrage 2

In [4] finden sich die in [1] vorgenommenen Eingaben wieder. Es gibt jedoch zwei unterschiedliche Anfragen HTTP. Es ist bekannt, dass der Speicher zwischen zwei Abfragen HTTP die Sitzung ist. Damit die zweite Abfrage die von der ersten übermittelten Werte abrufen kann, müssen diese in die Sitzung übernommen werden.

7.6.4. Die Serveraktion [Action05Get]

Die Aktion [Action05Get] ist die Aktion, die die anfängliche Einzelseite anzeigt. Ihr Code lautet wie folgt:


    [HttpGet]
    public ViewResult Action05Get()
    {
      ViewModel05 modèle = new ViewModel05();
      modèle.HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      return View(modèle);
}
  • Zeile 6: Die bereits behandelte Ansicht [Action05Get.cshtml] wird mit einer Vorlage vom Typ [ViewModel05] angezeigt;

7.6.5. Die Client-Aktion [Calculer]

Betrachten wir die Interaktionen des Benutzers mit den Sichten:

Der Link [1] ist ein JavaScript-Link:


<a href="javascript:calculer()">Calculer</a>

Die JavaScript-Funktion [calculer] befindet sich in der Datei [myScripts-05.js]:


  <script type="text/javascript" src="~/Scripts/myScripts-05.js"></script>

Der Code der JavaScript-Funktion [calculer] lautet wie folgt:


// Gesamtdaten
var content;
var loading;

function calculer() {
  // zunächst die Referenzen auf DOM
  var formulaire = $("#formulaire");
  // anschließend Validierung des Formulars
  if (!formulaire.validate().form()) {
    // Formular ungültig – abgeschlossen
    return;
  }
  // Manueller Ajax-Aufruf
  $.ajax({
    url: '/Premier/Action05FaireCalcul',
    type: 'POST',
    data: formulaire.serialize(),
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
    },
    error: function (jqXHR) {
      // Anzeige der Serverantwort
      content.html(jqXHR.responseText);
    }
  })
}

function retourSaisies() {
 ...
}

function effacer() {
  ...
}

// beim Laden des Dokuments
$(document).ready(function () {
  // die Referenzen der verschiedenen Komponenten der Seite werden abgerufen
  loading = $("#loading");
  content = $("#content");
  // Das animierte Bild wird ausgeblendet
  loading.hide();
});
  • Zur Erinnerung: JavaScript-Code wird immer clientseitig im Browser ausgeführt;
  • Zeile 44: Die Funktion JS wird ausgeführt, sobald das anfängliche Laden der Einzelseite abgeschlossen ist;
  • Zeile 46: Verweis auf das animierte Bild mit der ID [loading];
  • Zeile 47: Verweis auf den Bereich mit der ID [content]. Dieser Bereich empfängt die Teilansichten [Formulaire05, Success05, Failure05];
  • Zeilen 2–3: Die Variablen aus den Zeilen 46–47 werden als globale Variablen deklariert, damit andere Funktionen darauf zugreifen können. Die Suche nach Elementen auf einer Seite (Zeilen 46–47) ist mit einem gewissen Aufwand verbunden. Diese Suche sollte nicht wiederholt werden, wenn sich dies vermeiden lässt;
  • Zeile 5: die Funktion [calculer];
  • Zeile 7: Es wird eine Referenz auf das Formular abgerufen. Die Teilansicht [Formulaire05] hat ihm die ID [formulaire] zugewiesen;
  • Zeile 9: Diese Anweisung führt die clientseitigen Validatoren des Formulars aus. Genau das fehlte in der auf Seite 183 festgestellten Anomalie. Diese Methode wird von der Bibliothek [jquery.unobstrusive-ajax] bereitgestellt, die von der Einzelseite verwendet wird:

  <script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>

Die Anweisung gibt [false] zurück, wenn das Formular als ungültig erkannt wird;

  • Zeile 11: Der Ajax-Aufruf an den Server erfolgt nicht, wenn das Formular ungültig ist;
  • Zeilen 14–32: Der Ajax-Aufruf an den Server wird ausgeführt;
  • Zeile 15: Das Ziel von URL ist die Serveraktion [Action05FaireCalcul];
  • Zeile 16: Sie wird über ein [POST] angefordert;
  • Zeile 17: die übermittelten Werte. Dabei handelt es sich um die Formulareingaben, hier die Werte von A und B;
  • Zeilen 22–24: Bei erfolgreichem Ajax-Aufruf aktualisiert die Funktion [calculer] den Bereich mit der ID [content] mit dem vom Server gesendeten Datenstrom HTML.

Dieser Datenfluss HTML ist derjenige, der von der Aktion [Action05FaireCalcul] gesendet wird, auf die der Ajax-Aufruf abzielt. Der Code dieser serverseitigen Aktion lautet wie folgt:


    [HttpPost]
    public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
    {
      // Vorlage
      ViewModel05 modèle = new ViewModel05();
      // Berechnungszeit
      modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // Aktualisierung der Vorlage
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // Es wird ein Fehler zurückgegeben
        modèle.Erreurs = getListOfMessagesFor(ModelState);
        return PartialView("Failure05", modèle);
      }
...
}
  • Zeile 1: Die Aktion akzeptiert nur einen [post];
  • Zeile 2: Sie gibt eine Teilansicht zurück;
  • Zeile 2: Sie erhält als Parameter die über POST übermittelten Werte (postedData) und das Sitzungsmodell (session);
  • Zeile 5: Das Modell der Teilansicht wird erstellt;
  • Zeile 7: Es wird mit dem Berechnungszeitpunkt aktualisiert;
  • Zeile 9: Es wird versucht, die übermittelten Werte auf das Modell anzuwenden. Die Validatoren des Modells werden daraufhin ausgeführt. Man könnte sich fragen, warum man sich diese Mühe macht, obwohl die clientseitigen Validatoren die Ausführung von POST verhindern, wenn die eingegebenen Daten ungültig sind. Tatsächlich ist die Herkunft des POST ungewiss. Möglicherweise wurde er von einem Code erstellt, der nicht von uns stammt. Daher müssen stets serverseitige Überprüfungen durchgeführt werden;
  • Zeile 10: Es wird geprüft, ob die Validierungen erfolgreich waren;
  • Zeile 13: Ist das Modell ungültig, wird es mit einer Fehlerliste aktualisiert. Auf die interne Methode [getListOfMessagesFor], die der auf Seite 65 beschriebenen Methode [GetErrorMessagesFor] ähnelt, wird hier nicht näher eingegangen;
  • Zeile 14: Die Teilansicht [Failure05] wird mit ihrer Vorlage angezeigt. Der Code dieser Ansicht wird erneut aufgerufen;

@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
  @foreach (string msg in Model.Erreurs)
  {
    <li>@msg</li>
  }
</ul>
<p>
  <a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
  • Zeilen 7–12: Die Fehlerliste der Vorlage wird mithilfe eines <ul>-Tags angezeigt.

Zur Erinnerung: Die Funktion JS [calculer], diedem Server-Vorgang [Action05FaireCalcul] diesen Datenfluss HTML in den Bereich mit der ID [content] einfügt. Das ergibt in etwa Folgendes:

Setzen wir die Untersuchung des Codes der Aktion [Action05FaireCalcul] fort:


    [HttpPost]
    public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
    {
      // Vorlage
      ViewModel05 modèle = new ViewModel05();
...
      // Die Werte von A und B werden in die Sitzung übernommen
      session.A = modèle.A;
      session.B = modèle.B;
      // Bisher keine Fehler
      List<string> erreurs = new List<string>();
      // In jedem zweiten Fall wird ein Fehler simuliert
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        erreurs.Add("[erreur aléatoire]");
      }
      if (erreurs.Count != 0)
      {
        modèle.Erreurs = erreurs;
        return PartialView("Failure05", modèle);
      }
      // Berechnungen
      double A = double.Parse(modèle.A);
      double B = double.Parse(modèle.B);
      modèle.AplusB = string.Format("{0}", A + B);
      modèle.AmoinsB = string.Format("{0}", A - B);
      modèle.AmultipliéparB = string.Format("{0}", A * B);
      modèle.AdiviséparB = string.Format("{0}", A / B);
      // Ansicht
      return PartialView("Success05", modèle);
}
  • Zeile 7: Das Modell wurde als gültig deklariert;
  • Zeilen 8–9: Die eingegebenen Werte A und B werden in die Sitzung übernommen. Man möchte sie in der nachfolgenden Abfrage wiederfinden können;
  • Zeilen 11–22: In jedem zweiten Fall wird zufällig ein Fehler generiert;
  • Zeilen 24–29: Es werden die vier arithmetischen Operationen mit den eingegebenen reellen Zahlen durchgeführt;
  • Zeile 31: Die Teilsicht [Success05] wird zusammen mit ihrem Modell zurückgegeben. Diese Teilsicht sieht wie folgt aus:

@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h4>Résultats</h4>
<p>A+B=@Model.AplusB</p>
<p>A-B=@Model.AmoinsB</p>
<p>A*B=@Model.AmultipliéparB</p>
<p>A/B=@Model.AdiviséparB</p>
<p>
  <a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>

Zur Erinnerung: Die Funktion JS, die ursprünglich, die aus der Serveraktion [Action05FaireCalcul] stammt, diesen Datenstrom HTML in den Bereich mit der ID [content] einfügt. Das ergibt in etwa Folgendes:

7.6.6. Die Client-Aktion [Effacer]

Über den JavaScript-Link [Effacer] lässt sich das Formular in seinen ursprünglichen Zustand zurücksetzen:

Im Formular ist der Link JS [Effacer] wie folgt definiert:


<a href="javascript:effacer()">Effacer</a>

Die Funktion JS [effacer] ist in der Datei [myScripts-05.js] wie folgt definiert:


// Gesamtdaten
var content;
var loading;

function calculer() {
...
}

function retourSaisies() {
...
}

function effacer() {
  // zuerst die Referenzen auf dem DOM
  var formulaire = $("#formulaire");
  var A = $("#A");
  var B = $("#B");
  // den Eingaben werden gültige Werte zugewiesen
  A.val("0");
  B.val("0");
  // anschließend wird das Formular freigegeben, um
  // etwaige Fehlermeldungen
  formulaire.validate().form();
  // Anschließend werden den Eingabefeldern leere Zeichenfolgen zugewiesen
  A.val("");
  B.val("");
}

// beim Laden des Dokuments
$(document).ready(function () {
  // ruft man die Referenzen der verschiedenen Komponenten der Seite ab
  loading = $("#loading");
  content = $("#content");
  // das animierte Bild wird ausgeblendet
  loading.hide();
});
  • Zeilen 15–17: Es werden Referenzen auf verschiedene Elemente von DOM (Document Object Model) abgerufen;
  • Zeilen 19–20: Es werden gültige Werte in die Eingabefelder für die Zahlen A und B eingegeben;
  • Zeile 23: Die clientseitigen Validatoren werden ausgeführt. Da die Werte von A und B gültig sind, werden dadurch eventuelle Fehlermeldungen, die möglicherweise angezeigt würden, ausgeblendet;
  • Zeilen 25–26: In die Eingabefelder für die Zahlen A und B werden leere Zeichenfolgen eingegeben;

7.6.7. Die Client-Aktion [Retour aux Saisies]

Über den JavaScript-Link [Retour aux Saisies] gelangt man nach Erhalt der Ergebnisse zurück zum Formular:

Im Formular ist der Link JS [Retour aux Saisies] wie folgt definiert:


  <a href="javascript:retourSaisies()">Retour aux saisies</a>

Die Funktion JS [retourSaisies] ist in der Datei [myScripts-05.js] wie folgt definiert:


// globale Daten
var content;
var loading;

function calculer() {
...
}

function retourSaisies() {
  // Es wird manuell ein Ajax-Aufruf durchgeführt
  $.ajax({
    url: '/Premier/Action05RetourSaisies',
    type: 'POST',
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
      // IMPORTANT !! Validierung
      $.validator.unobtrusive.parse($("#formulaire"));
    },
    error: function (jqXHR) {
      content.html(jqXHR.responseText);
    }
  })
}

function effacer() {
...
}

// beim Laden des Dokuments
$(document).ready(function () {
  // Die Referenzen der verschiedenen Komponenten der Seite werden abgerufen
  loading = $("#loading");
  content = $("#content");
  // Das animierte Bild wird ausgeblendet
  loading.hide();
});
  • Zeilen 11–29: ein Ajax-Aufruf;
  • Zeile 12: die Zielfunktion URL;
  • Zeile 13: Sie wird durch den Befehl HTTP POST angefordert. Es handelt sich um einen POST ohne übermittelte Parameter. Aus diesem Grund findet sich keine Zeile vom Typ:
    data: formulaire.serialize(),

im Ajax-Aufruf;

  • Zeile 14: Der vom Server erwartete Datenstrom ist ein HTML;
  • Zeilen 18–20: Dieser Datenstrom HTML dient dazu, den Bereich mit der ID [content] zu aktualisieren;

Die Serveraktion [Action05RetourSaisies] lautet wie folgt:


    [HttpPost]
    public PartialViewResult Action05RetourSaisies(SessionModel session)
    {
      // Ansicht
      return PartialView("Formulaire05", new ViewModel05() { A = session.A, B = session.B });
}
  • Zeile 2: Die Aktion erhält als Parameter das Sitzungsmodell, in dem wir zuvor die eingegebenen Werte für A und B gespeichert haben;
  • Zeile 5: Wir geben die Teilansicht [Formulaire05] mit einem Modell vom Typ [ViewModel05] zurück, wobei wir darauf achten, die Felder A und B mit den aus der Sitzung übernommenen Werten für A und B zu initialisieren;

Kehren wir nun zum Code der JavaScript-Funktion [retourSaisies] zurück:


function retourSaisies() {
  // Man führt manuell einen Ajax-Aufruf durch
  $.ajax({
    url: '/Premier/Action05RetourSaisies',
    type: 'POST',
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
      // IMPORTANT !! Validierung
      $.validator.unobtrusive.parse($("#formulaire"));
    },
    error: function (jqXHR) {
      content.html(jqXHR.responseText);
    }
  })
}
  • Zeile 13: Die Methode, die ausgeführt wird, wenn der Ajax-Aufruf abgeschlossen ist;
  • Zeile 14: Das animierte Ladebild wird ausgeblendet;
  • Zeile 16: Eine für mich etwas undurchsichtige Anweisung, die ich im Internet gefunden habe, um das folgende Problem zu beheben: In dem Formular, das über den Link [Retour aux saisies] angezeigt wird, funktionierten die clientseitigen Validatoren nicht mehr. Bei der Suche nach Informationen zur Bibliothek JS [jquery.unobtrusive-ajax] habe ich die Lösung in Zeile 16 gefunden. Sie analysiert das Formular, möglicherweise um die clientseitigen Validatoren zu aktivieren.

7.7. Eine Anwendung ASP.NET im Internet verfügbar machen

Siehe Abschnitt 9.26.

7.8. Erstellung einer nativen Android-App aus einer Single-Page-Anwendung APU

Siehe Abschnitt 9.27.