Skip to content

7. Ajaxifizierung einer ASP.NET-MVC-Anwendung

7.1. Die Rolle von AJAX in einer Webanwendung

Bislang weisen die Lernbeispiele, die wir behandelt haben, folgende Architektur auf:

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

  • eine Anfrage an die Webanwendung;
  • empfängt die Ansicht [View2] und zeigt sie anstelle der Ansicht [View1] an.

Dies ist das klassische Muster:

  • Anfrage vom Browser;
  • der Webserver generiert als Antwort an den Client eine Ansicht;
  • 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 macht weiterhin das, was er am besten kann – eine HTML-Ansicht anzeigen –, wird nun jedoch von JavaScript gesteuert, das in die angezeigte HTML-Ansicht eingebettet ist. Der Ablauf ist wie folgt:

  • in [1] tritt ein Ereignis auf der im Browser angezeigten Seite ein (Klick auf eine Schaltfläche, Änderung des Textes usw.). Dieses Ereignis wird von in die Seite eingebettetem JavaScript (JS) abgefangen;
  • in [2] sendet der JavaScript-Code eine HTTP-Anfrage, genau wie es der Browser getan hätte. Die Anfrage ist asynchron: Der Benutzer kann weiterhin mit der Seite interagieren, ohne blockiert zu werden, während er auf die HTTP-Antwort wartet. Die Anfrage folgt dem Standard-Verarbeitungsablauf. Nichts (oder nur sehr wenig) unterscheidet sie von einer Standardanfrage;
  • In [3] wird eine Antwort an den JavaScript-Client gesendet. Anstelle einer vollständigen HTML-Ansicht wird in der Regel eine teilweise HTML-Ansicht, ein XML-Feed oder JSON (JavaScript Object Notation) gesendet;
  • In [4] ruft JavaScript diese Antwort ab und verwendet sie, um einen Bereich der angezeigten HTML-Seite zu aktualisieren.

Für den Benutzer ändert sich die Ansicht, da sich das Gesehene verändert hat. Es findet jedoch kein vollständiges Neuladen der Seite statt; stattdessen wird die angezeigte Seite nur teilweise aktualisiert. Dies trägt dazu bei, die Seite flüssiger und interaktiver zu gestalten: Da keine vollständige Neuladung der Seite erfolgt, können wir Ereignisse verarbeiten, die zuvor nicht behandelt werden konnten. Zum Beispiel, indem wir dem Benutzer eine Liste mit Optionen anbieten, während er Zeichen in ein Eingabefeld tippt. Mit jedem neuen eingegebenen Zeichen wird eine AJAX-Anfrage an den Server gesendet, der dann weitere Vorschläge zurückgibt. Ohne AJAX war diese Art der Eingabeunterstützung zuvor unmöglich. Wir konnten nicht bei jedem eingegebenen Zeichen eine neue Seite neu laden.

7.2. Grundlagen von jQuery und JavaScript

Wir haben die jQuery-JavaScript-Bibliothek häufig in unsere Seiten eingebunden. So finden Sie beispielsweise die folgende Zeile:


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

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

Die Ajax-Technologie von ASP.NET MVC nutzt jQuery. Wir werden selbst einige jQuery-Skripte schreiben. Lassen Sie uns daher nun die Grundlagen von jQuery behandeln, die Sie kennen müssen, um die Skripte in diesem Kapitel zu verstehen.

Wir erstellen ein neues Projekt [Example-04] innerhalb unserer [Examples]-Lösung:

Um Ajax mit ASP.NET MVC zu verwenden, muss die folgende Zeile in der Konfigurationsdatei [Web.config] vorhanden sein [1]:


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

Zeile 3 aktiviert die Verwendung von Ajax in ASP.NET-Ansichten. Sie ist standardmäßig vorhanden.

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

Diese Datei enthält 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: Importieren von jQuery (passen Sie die Version an Ihre Visual Studio-Version an);
  • Zeilen 10–12: ein Seitenelement mit der ID [element1]. Wir werden mit diesem Element arbeiten.

Zeigen Sie diese Datei im Google Chrome-Browser an [4] und [5]:

Drücke in Google Chrome [Strg-Umschalt-I], um die Entwicklertools zu öffnen [6]. Auf der Registerkarte [Konsole] [7] kannst du JavaScript-Code ausführen. Nachfolgend findest du JavaScript-Befehle, die du eingeben kannst, sowie Erläuterungen zu ihrer Funktion.

JS
Ergebnis
$("#element1")
: Gibt die Sammlung aller Elemente mit der ID [element1] zurück, also normalerweise eine Sammlung von 0 oder 1 Element, da es auf einer HTML-Seite keine zwei identischen IDs geben kann.
$("#element1").text("blabla")
: setzt den Text [blabla] für alle Elemente in der Sammlung. Dies ändert den auf der Seite angezeigten Inhalt
$("#element1").hide()
blendet die Elemente in 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 CSS-Attribut style='display: none;' hat, wodurch das Element ausgeblendet wird.
$("#element1").show()
: zeigt die Elemente der Sammlung an. Der Text [blabla] erscheint wieder. Das CSS-Attribut style='display: block;' ist für diese Anzeige verantwortlich.
$("#element1").attr('style','color: red')
: Setzt ein Attribut für alle Elemente in der Sammlung. Das Attribut lautet hier [style] und sein Wert ist [color: red]. Der Text [blabla] wird rot.
Tabelle
Wörterbuch

Beachten Sie, dass sich die URL des Browsers während all dieser Vorgänge nicht geändert hat. Es fand keine Kommunikation mit dem Webserver statt. Alles geschieht innerhalb des Browsers. Sehen wir uns nun den Quellcode der Seite an:

Image


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

Dies ist der ursprüngliche Text. Er spiegelt nicht die Änderungen wider, die in den Zeilen 10–12 am Element vorgenommen wurden. Es ist wichtig, dies beim Debuggen von JavaScript zu beachten. In solchen Fällen ist es oft nicht notwendig, den Quellcode der angezeigten Seite einzusehen. Um den Quellcode der aktuell angezeigten Seite einzusehen, gehen Sie wie folgt vor:

Image

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

7.3. Aktualisieren einer Seite mit einem HTML-Feed

7.3.1. Die Ansichten

Wir werden die folgende Anwendung untersuchen:

  • in [1] die Ladezeit der Seite;
  • in [2] führen wir die vier arithmetischen Operationen mit zwei reellen Zahlen A und B durch;
  • in [3] wird die Antwort des Servers in einem Bereich der Seite angezeigt;
  • in [4] die Rechenzeit. Diese unterscheidet sich von der Ladezeit der Seite [5]. Letztere entspricht [1], was zeigt, dass der Bereich [6] nicht neu geladen wurde. Außerdem hat sich die URL der Seite [7] nicht geä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 ViewModels;
  • Zeile 5: Initialisierung der Ladezeit der Ansicht;
  • Zeile 6: Anzeige der Ansicht [Action10Get.cshtml] und ihres Modells.

Das Modell [ ViewModel01] sieht wie folgt aus:


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
  {
    // form
    [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; }
 
    // results
    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: den Wert A aus dem Formular;
  • Zeilen 15–18: der Wert B aus dem Formular;
  • Zeilen 21–24: die Ergebnisse der vier arithmetischen Operationen mit A und B;
  • Zeile 25: der Text eines eventuellen Fehlers;
  • Zeile 26: die Zeit, zu der die Ansicht im Browser geladen wurde;
  • Zeile 27: der Zeitpunkt, zu dem die Felder in den Zeilen 21–24 berechnet wurden;
  • Zeile 7: Diese Ansichtsvorlage ist gleichzeitig eine Aktionsvorlage. Felder, die nicht vom Browser übermittelt werden, sind von dieser ausgeschlossen.

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 einem Typ [ViewModel01];
  • Zeile 21: jQuery wird sowohl für die Validierung 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: zeigt die Ladezeit der Ansicht an;
  • Zeile 35: ein Ajax-Formular – darauf kommen wir später zurück;
  • Zeilen 40–41: Beschriftungen für die Eingabefelder A und B;
  • Zeilen 46–47: Eingabefelder für die Zahlen A und B;
  • Zeilen 50–51: Fehlermeldungen für die Zahleneingaben A und B;
  • Zeile 56: die Schaltfläche zum Absenden des Formulars. Das Formular wird über eine Ajax-Anfrage übermittelt;
  • Zeile 57: ein Lade-Symbol, das während der Ajax-Anfrage angezeigt wird;
  • Zeile 58: ein Link zum Absenden des Formulars mit einer Ajax-Anfrage;
  • Zeile 62: ein div-Tag mit der ID [results]. Hier platzieren wir die vom Webserver zurückgegebene HTML-Ausgabe.

Diese Ansicht zeigt die folgende Seite an:

Image

Sehen wir uns nun den Code an, der die Ajax-Funktionalität des Formulars steuert:


...
@{
  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] verwenden wir [@Ajax.BeginForm]. Diese Methode unterstützt viele Überladungen. Die verwendete hat die folgende Signatur:

Ajax.BeginForm(string ActionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string,object> htmlAttributes)

Hier verwenden wir die folgenden tatsächlichen Parameter:

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

null: Es sind keine Routeninformationen anzugeben,

ajaxOpts: die Optionen für den Ajax-Aufruf. Diese wurden in den Zeilen 6–10 definiert,

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

Die verwendeten Ajax-Optionen lauten wie folgt:

  • Zeile 8: die Ziel-URL der Ajax-HTTP-Anfrage;
  • Zeile 7: Methode der Ajax-HTTP-Anfrage;
  • Zeile 6: ID des Seitenbereichs, der durch die Antwort auf die Ajax-Anfrage aktualisiert wird;
  • Zeile 9: die ID des Seitenbereichs, der während der Ajax-Anfrage angezeigt wird – in der Regel ein Lade-Symbol. Hier wird Zeile 20 angezeigt. Sie enthält ein animiertes Bild, das einen Ladezustand 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 HTML-Code 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. Beachten Sie die [data-ajax-attr]-Attribute, die die Werte der Felder im [AjaxOptions]-Objekt widerspiegeln, das mit der Ajax-Anfrage verknüpft ist. Diese Attribute werden von der Ajax-Bibliothek verwaltet. Ohne sie sieht das <form>-Tag wie folgt aus:


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

Dies ist ein Standard-HTML-Formular. 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-HTTP-Anfrage verarbeitet, lautet wie folgt:


    [HttpPost]
    public PartialViewResult Action01Post(FormCollection postedData, SessionModel session)
    {
      // wait simulation
      Thread.Sleep(2000);
      // action model instantiation
      ViewModel01 modèle = new ViewModel01();
      // calculation time
      modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // model update
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // returns an error
        modèle.Erreur = getErrorMessagesFor(ModelState);
        return PartialView("Action01Error", modèle);
      }
      // every other time, an error is simulated
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        modèle.Erreur = "[erreur aléatoire]";
        return PartialView("Action01Error", modèle);
      }
      // calculations
      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);
      // view
      return PartialView("Action01Success", modèle);
}

  • Zeile 1: Die Aktion verarbeitet nur einen [POST];
  • Zeile 2: Sie akzeptiert das folgende Aktionsmodell:
    • [FormCollection postedData]: die Menge der durch die Ajax-POST-Anfrage übermittelten Werte,
    • [SessionModel session]: die Sitzungselemente. Hier verwenden wir eine in Abschnitt 4.10 beschriebene Technik;
  • Zeile 2: Die Aktion gibt ein HTML-Fragment statt einer vollständigen HTML-Seite zurück;
  • Zeile 5: Wir legen künstlich eine Pause von zwei Sekunden ein, um eine lange Ajax-Aktion zu simulieren;
  • Zeile 7: Ein Modell vom Typ [ViewModel01] wird instanziiert;
  • Zeile 9: Die Berechnungszeit wird initialisiert;
  • Zeile 11: Wir versuchen, das [ViewModel01]-Modell mit den übermittelten Werten zu aktualisieren. Zur Erinnerung: Es gibt zwei Werte: die Zahlen A und B;
  • Zeile 12: Wir prüfen, ob diese Aktualisierung erfolgreich war;
  • Zeile 15: Wenn ein Fehler auftritt, wird das Feld [Error] des Modells gefüllt;
  • Zeile 16: Eine Teilansicht [Action01Error.cshtml], die das Modell [ViewModel01] verwendet, wird gerendert;
  • Zeilen 19–24: In jedem zweiten Durchgang wird ein Fehler simuliert;
  • Zeile 19: Es wird eine zufällige Ganzzahl im Bereich [0,1] generiert. Der Zufallszahlengenerator wird aus der Sitzung übernommen;
  • Zeile 20: Wenn der generierte Wert 0 ist, wird ein Fehler simuliert;
  • Zeile 22: Die Fehlermeldung wird in das Modell eingefügt;
  • Zeile 23: Eine Teilansicht [Action01Error.cshtml] unter Verwendung des Modells [ViewModel01] wird gerendert;
  • Zeilen 26–29: Es werden arithmetische Berechnungen mit den Zahlen A und B durchgeführt, und die Ergebnisse werden als Zeichenfolgen in das View-Modell eingefügt;
  • Zeile 31: Eine Teilansicht [Action01Success.cshtml] wird unter Verwendung des Modells [ViewModel01] gerendert;

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>

Beachten Sie, dass dieser HTML-Ausschnitt als Antwort auf die Ajax-HTTP-Anfrage vom Typ POST gesendet und auf der Seite in dem Bereich mit der ID [results] platziert wird. All 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 ist ein Beispiel für eine Antwort mit einem Fehler:

Image

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 dieser Teil-HTML-Stream wird als Antwort auf die Ajax-HTTP-Anfrage vom Typ POST gesendet und auf der Seite im Bereich mit der ID [results] platziert:

Image

7.3.6. G -Session-Management

Wir haben gesehen, dass [Action01Post] die Sitzung verwendet. 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:


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

Die Sitzung wird in [Application_Start] einem Modell zugeordnet:


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

Die Klasse [SessionModelBinder] wurde beschrieben.

7.3.7. Verwaltung des Platzhalterbildes


@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 die Region mit der ID [loading] in Zeile 7 nach einer Sekunde [Zeile 8] angezeigt. Diese Region ist das Bild aus Zeile 21, das ursprünglich ausgeblendet war. Dies führt zu folgender Benutzeroberfläche:

Betrachten wir den Link [Calculate] 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 [Berechnen] löst die Ausführung der JS-Funktion [postForm] aus. Diese Funktion ist in der Datei [myScripts-01.js] in Zeile 5 definiert. Das Skript lautet wie folgt:

function postForm() {
  // on fait un appel Ajax à la main avec JQuery
  var loading = $("#loading");
  var formulaire = $("#formulaire");
  var résultats = $('#results');
  $.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) {
    //Use the Globalization plugin to parse the value        
    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 dienen der Internationalisierung der Seiten. Wir werden hier nicht noch einmal darauf eingehen. In den Zeilen 1–17 führen wir den Ajax-Aufruf manuell durch, was im Fall der Schaltfläche [Berechnen] zuvor von der Ajax-Bibliothek des Projekts übernommen wurde. Dazu verwenden wir die jQuery-Bibliothek des Projekts.

  • Zeile 3: ein Verweis auf die Komponente mit der ID [loading]. [$("#loading")] gibt die Sammlung von Elementen mit der ID [loading] zurück. Es gibt nur eines;
  • Zeile 4: Ein Verweis auf die Komponente mit der ID [form];
  • Zeile 5: ein Verweis auf die Komponente mit der ID [results];
  • Zeile 6: Der Ajax-Aufruf mit seinen Optionen;
  • Zeile 7: Die Ziel-URL des Ajax-Aufrufs;
  • Zeile 8: Die verwendete HTTP-Methode;
  • Zeile 9: die gesendeten Daten. [form.serialize] erstellt den POST-String [A=val1&B=val2] für das Formular mit der ID [form];
  • Zeile 10: Der erwartete Datentyp in der Antwort. Wir wissen, dass der Server einen HTML-Stream zurückgibt;
  • Zeile 11: Die Methode, die beim Start der Anfrage ausgeführt werden soll. Hier legen wir fest, dass die Komponente mit der ID [loading] angezeigt werden soll. Dies ist das animierte Lade-Bild;
  • Zeile 12: Die Methode, die ausgeführt werden soll, wenn die Ajax-Anfrage erfolgreich ist. Der Parameter [data] ist die vollständige Antwort vom Server. Wir wissen, dass es sich um einen HTML-Stream handelt;
  • Zeile 13: Wir blenden die Ladeanzeige aus;
  • Zeile 14: Wir aktualisieren die Komponente mit der ID [results] mit dem HTML-Code aus dem Parameter [data].

Der Leser ist eingeladen, den Link [Calculate] zu testen. Er funktioniert genau wie die Schaltfläche [Calculate], mit Ausnahme der Fehlermeldung „ .” Sobald dieser Link verwendet wurde, können ungültige Werte für A und B eingegeben werden:

  • In [1] und [2] haben wir ungültige Werte eingegeben. Diese werden von den clientseitigen Validatoren markiert;
  • in [3] haben wir auf den Link [Berechnen] geklickt;
  • in [4] erfolgte ein [POST], da wir die Antwort [4] erhalten haben.

Wenn die Werte ungültig sind und wir auf die Schaltfläche [Berechnen] klicken, erfolgt keine [POST]-Anfrage an den Server. Im gleichen Szenario löst die Verwendung des Links [Berechnen] die [POST]-Anfrage an den Server aus. Es gibt also ein Verhalten der Schaltfläche [Berechnen], das wir mit dem Link [Berechnen] nicht reproduzieren konnten. Anstatt zu versuchen, dieses Problem jetzt zu lösen, werden wir es für ein späteres Beispiel aufheben, das auch ein weiteres Problem der clientseitigen Validierung veranschaulichen wird.

7.4. Aktualisieren einer HTML-Seite mit einem JSON-Feed

Im vorherigen Beispiel hat der Webserver auf die Ajax-HTTP-Anfrage mit einem HTML-Stream geantwortet. Dieser Stream enthielt Daten mit HTML-Formatierung. Wir werden das vorherige Beispiel noch einmal aufgreifen, diesmal jedoch mit JSON-Antworten (JavaScript Object Notation), die nur die Daten enthalten. Der Vorteil besteht darin, dass weniger Bytes übertragen werden.

7.4.1. Die Aktion [Action02Get]

Die Aktion [Action02Get] wird der Einstiegspunkt für die neue 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 Ajax-Aufrufoptionen;
  • Zeile 10: die JS-Funktion, die beim Start der Anfrage ausgeführt werden soll. Diese Funktion ist in der in Zeile 24 referenzierten JS-Datei definiert;
  • Zeile 11: die JS-Funktion, die ausgeführt werden soll, wenn die Anfrage fehlschlägt;
  • Zeile 12: die JS-Funktion, die ausgeführt werden soll, wenn die Anfrage erfolgreich ist;
  • Zeile 13: die JS-Funktion, die ausgeführt werden soll, nachdem die Ajax-Anfrage ihr Ergebnis (Fehlschlag oder Erfolg) zurückgegeben hat;
  • Zeilen 40–43: ein Bereich mit der ID [header];
  • Zeilen 44–49: ein Bereich mit der ID [results]. Hier werden die Ergebnisse der vier arithmetischen Operationen angezeigt;
  • Zeilen 50–52: eine ID-Region [error]. Hier werden etwaige Fehlermeldungen angezeigt.

7.4.2. Die Aktion [Action02Post]

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


[HttpPost]
    public JsonResult Action02Post(FormCollection postedData, SessionModel session)
    {
      // wait simulation
      Thread.Sleep(2000);
      // model validation
      ViewModel02 modèle = new ViewModel02();
      // loading and calculation times
      string HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      string HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // model update
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // returns an error
        return Json(new { Erreur = getErrorMessagesFor(ModelState), HeureCalcul = HeureCalcul });
      }
      // every other time, an error is simulated
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        // returns an error
        return Json(new { Erreur = "[erreur aléatoire]", HeureCalcul = HeureCalcul });
      }
      // calculations
      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);
      // we return the results
      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. Text im JSON-Format;
  • Zeile 16: Die Informationen werden als anonyme Klasseninstanz zurückgegeben, die in JSON serialisiert wurde. Die Methode [getErrorMessagesFor] wurde bereits vorgestellt. Die an den Browser gesendete JSON-Zeichenkette hat folgende Form:
{"Erreur":"[erreur aléatoire]","HeureCalcul":"05:31:37"}
  • Zeile 31: Gleicher Ansatz für arithmetische Ergebnisse. Diesmal hat die an den Browser gesendete JSON-Zeichenkette 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

Sehen wir uns die Konfiguration des Ajax-Aufrufs in der HTML-Seite an, die an den Browser des Clients 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 „=“-Zeichen) referenzierten JS-Funktionen sind in der folgenden Datei [myScripts-02.js] definiert:


// global data
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() {
...
}
 
// document loading
$(document).ready(function () {
  formulaire = $("#formulaire");
  entete = $("#entete");
  loading = $("#loading");
  erreur = $("#erreur");
  résultats = $('#résultats');
  heureCalcul = $("#heureCalcul");
  msg = $("#msg");
  AplusB = $("#AplusB");
  AmoinsB = $("#AmoinsB");
  AmultipliéparB = $("#AmultipliéparB");
  AdiviséparB = $("#AdiviséparB");
 
  // hide certain page elements
  entete.hide();
  résultats.hide();
  erreur.hide();
});
 
// start
function OnBegin() {
....
}
 
// end of query
function OnComplete() {
...
}
 
// success
function OnSuccess(data) {
....
}
 
// error
function OnFailure(request, error) {
...
}
  • Zeile 19: Die JS-Funktion, die ausgeführt wird, wenn die Seite im Browser vollständig geladen ist;
  • Zeilen 20–30: Wir rufen die Referenzen aller Seitenkomponenten ab, die uns interessieren. Die Suche nach einer Komponente auf einer Seite verursacht Kosten, daher ist es am besten, dies nur einmal zu tun;
  • Zeilen 33–35: Die Komponenten [header], [results] und [loading] werden ausgeblendet;

Wenn die Ajax-Anfrage startet, wird die folgende Funktion ausgeführt:


// start
function OnBegin() {
  // wait signal on
  loading.show();
  // hide certain page elements
  entete.hide();
  résultats.hide();
  erreur.hide();
}
  • Zeile 4: Die Komponente [loading] wird angezeigt. Dies ist das animierte Bild;
  • Zeilen 6–8: Die Komponenten [header], [results] und [error] werden ausgeblendet;

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


// réussite
function OnSuccess(data) {
  // affichage résultats
  heureCalcul.text(data.HeureCalcul);
  entete.show();
  if (data.Erreur != '') {
    msg.text(data.Erreur);
    erreur.show();
    return;
  }
  // pas d'erreur
  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, müssen Sie sich an die beiden JSON-Strings erinnern, die als Antwort an den Browser gesendet werden können:

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

im Falle eines Fehlers; andernfalls die Zeichenfolge:

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

Wenn wir diese Zeichenfolge [data] nennen, erhält man den Wert des Feldes [Error] je nach Wunsch mit der Notation [data.Error] oder [data["Error"]]. Dasselbe gilt für die anderen Felder in der JSON-Zeichenfolge. Um einer Komponente mit der ID X unformatierten Text zuzuweisen, schreiben wir außerdem [X.text(string)]. Kehren wir zum Code für die Funktion [OnSuccess] zurück:

  • Zeile 2: [data] ist die empfangene JSON-Zeichenkette;
  • Zeile 4: Die Komponente [calculationTime] erhält ihren Wert;
  • Zeile 5: Die Komponente [entete] wird angezeigt;
  • Zeile 6: überprüft das Feld [Error] der JSON-Zeichenkette;
  • Zeile 7: Die Komponente [msg] erhält ihren Wert;
  • Zeile 8: Die Komponente [error] wird angezeigt;
  • Zeile 9: Das war's für den Fehlerfall;
  • Zeile 12: Die Komponente [AplusB] erhält ihren Wert;
  • Zeile 13: Die Komponente [AminusB] erhält ihren Wert;
  • Zeile 14: Die Komponente [AmultipliedbyB] erhält ihren Wert;
  • Zeile 15: Der Komponente [AdividedbyB] wird ein Wert zugewiesen;
  • Zeile 16: Die Komponente [results] wird angezeigt.

Die Funktion [ OnFailure] wird ausgeführt, wenn die Ajax-HTTP-Anfrage fehlschlägt. Dieser Fehler wird durch den vom Server zurückgegebenen HTTP-Statuscode ermittelt. Beispielsweise weist der Code 500 [Internal Server Error] darauf hin, dass der Server die Anfrage nicht verarbeiten konnte. Die Funktion [OnFailure] lautet wie folgt:


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

Wir zeigen einfach ein Dialogfeld mit dem aufgetretenen Fehler an. In der Praxis sollten wir genauer sein. Wir werden bald 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.


// end of query
function OnComplete() {
  // wait signal off
  loading.hide();
}

Beachten Sie, dass es die Konfiguration des Ajax-Aufrufs in der Ansicht [Action02Get.cshtml] ist, die den Aufruf dieser verschiedenen Funktionen bewirkt:


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

Der HTML-Code für den Link [Calculate] in der Ansicht [Action02Get.cshtml] lautet wie folgt:


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

Die JS-Funktion [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() {
  // make a manual Ajax call with JQuery
  $.ajax({
    url: '/Premier/Action02Post',
    type: 'POST',
    data: formulaire.serialize(),
    dataType: 'json',
    beforeSend: OnBegin,
    success: OnSuccess,
    error: OnFailure,
    complete: OnComplete
  })
}

Wir sind bereits auf ähnlichen Code gestoßen.

  • Zeile 4: Ziel-URL des Ajax-Aufrufs;
  • Zeile 5: Vom Ajax-Aufruf verwendete HTTP-Methode;
  • Zeile 6: Gesendete Werte. Dies sind die Ergebnisse der Serialisierung der Formularwerte. Das Formular, das durch die ID [form] identifiziert wird, wird durch die Variable [form] referenziert. [data] ist eine Zeichenkette in der Form [A=val1&B=val2];
  • Zeile 7: Erwarteter Antwortformat-Typ. Dies ist eine JSON-Zeichenkette;
  • Zeile 8: JS-Funktion, die beim Start des Ajax-Aufrufs ausgeführt werden soll;
  • Zeile 9: JS-Funktion, die ausgeführt werden soll, wenn der Ajax-Aufruf erfolgreich ist;
  • Zeile 10: JS-Funktion, die ausgeführt werden soll, wenn der Ajax-Aufruf fehlschlägt;
  • Zeile 11: JS-Funktion, die ausgeführt werden soll, sobald die Serverantwort empfangen wurde, unabhängig davon, ob es sich um einen Erfolg oder einen Fehler handelt.

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

Im vorherigen Beispiel sieht die Funktion [OnFailure] wie folgt aus:


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

Im Allgemeinen liefert die Anzeige des [error]-Objekts keine nützlichen Informationen. Wenn Sie einen mit jQuery durchgeführten Ajax-Aufruf verwenden, können Sie die folgende [OnFailure]-Methode nutzen;


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

Das jQuery-Objekt [jqXHR] verfügt über folgende Eigenschaften:

  • responseText: der Text der Serverantwort;
  • status: der vom Server zurückgegebene Fehlercode;
  • statusText: der mit diesem Fehlercode verbundene Text.

  • Zeile 3: Wir zeigen den Fehlercode und die dazugehörige Meldung an;
  • Zeile 4: Wir speichern die HTML-Antwort des Servers in der Komponente mit der ID [msg];
  • Zeile 5: Wir zeigen den Bereich mit der ID [error] an.

Um diese Fehlerbehandlung zu testen, erzeugen wir künstlich eine Ausnahme in der Aktion [Action02Post]:


    [HttpPost]
    public JsonResult Action02Post(FormCollection postedData, SessionModel session)
    {
      // an artificial exception to test the Ajax call error function
      throw new Exception();
      // wait simulation
      Thread.Sleep(2000);
      // model validation
...

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

Wir erhalten die folgenden Antworten [1] und [2]:

Die Serverantwort ermöglicht es uns zu erkennen, in welcher Zeile des Servercodes der Fehler aufgetreten ist. Dies ist oft eine nützliche Information. Von nun an werden wir diese Technik nutzen, um Fehler in Ajax-Aufrufen zu behandeln.

7.5. Single-Page-Webanwendung

Die Ajax-Technologie ermöglicht es uns, Single-Page-Anwendungen zu erstellen:

  • Die erste Seite wird über eine Standard-Browseranfrage geladen;
  • die nachfolgenden Seiten werden über Ajax-Aufrufe abgerufen. Dadurch ändert der Browser niemals seine URL und lädt nie eine neue Seite. Diese Art von Anwendung wird als Single-Page-Anwendung (SPA) bezeichnet.

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

  • In [1] zeigt die Aktion [Action03Get] die erste Seite, Seite 1, an;
  • in [2] ermöglicht ein Link die Navigation zu Seite 2 über einen Ajax-Aufruf;
  • in [3] hat sich die URL nicht geändert. Die angezeigte Seite ist Seite 2;
  • in [4] ermöglicht uns ein Link, über einen Ajax-Aufruf zu Seite 1 zurückzukehren;
  • In [5] hat sich die URL nicht geändert. Es wird Seite 1 angezeigt.

Der Code für die Aktion [Action03Get] lautet wie folgt:


    [HttpGet]
    public ViewResult Action03Get()
    {
      return View();
}

  • Zeile 4: Die Ansicht [Action03Get.cshtml] wird angezeigt.

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


@{
  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]. Dies ist das Element, in dem die verschiedenen Seiten angezeigt werden;
  • Zeile 17: Standardmäßig wird die Seite [Page1.cshtml] zuerst angezeigt.

Die Seite [Page1.cshtml] sieht wie folgt aus:


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

  • Zeile 1: Der Seitentitel, um die Seite von Seite 2 zu unterscheiden;
  • Zeile 3: ein Ajax-Link mit den folgenden Parametern:
    • die Link-Bezeichnung [Seite 2];
    • die Zielaktion des Links [Action04];
    • die Parameter der angeforderten URL. Dies lautet [/Premier/Action04?Page=2];
  • die Ajax-Aufrufoptionen. Hier nur die ID des Bereichs, der mit der Antwort des Servers aktualisiert werden soll. Für die anderen Optionen werden Standardwerte verwendet, sofern vorhanden. Die Standard-HTTP-Methode ist GET.

Schauen wir uns an, was passiert, wenn der Link angeklickt wird. Die URL [/Premier/Action04?Page=2] wird mit einer GET-Anfrage angefordert. Die Aktion [Action04] wird dann 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 partiellen HTML-Stream zurück;
  • Zeile 2: Die Aktion verwendet die Zeichenfolge [page] als Vorlage. Wir wissen, dass die URL diese Information enthält: [/Premier/Action04?Page=2]. Beachten Sie, dass bei der Vorlage die Groß-/Kleinschreibung keine Rolle spielt;
  • Zeilen 4–8: [view] erhält den Wert [Page2];
  • Zeile 9: Die Teilansicht [Page2.cshtml] wird gerendert.

Die partielle Ansicht [Page2.cshtml] sieht wie folgt aus:


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

Der Server gibt daher den obigen HTML-Stream als Antwort auf die Ajax-GET-Anfrage [/Premier/Action04?Page=2] zurück. Zur Erinnerung: Diese Ajax-Anfrage verwendet diese Antwort, um den Bereich mit der ID [content] zu aktualisieren (Zeile 3 unten):


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

Dies führt zu folgender neuer Anzeige [1]:

Nach derselben Logik sehen wir, dass ein Klick auf den Link [Seite 1] in [1] die Anzeige [2] hervorruft.

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

Dank JavaScript, das in HTML-Seiten eingebettet ist und im Browser ausgeführt wird, können wir Code auf den Browser auslagern und die folgende Architektur realisieren:

  • in [1] ist die ASP.NET-MVC-Webschicht zu einer Webschnittstelle für den Zugriff auf Daten geworden, die typischerweise in einer Datenbank gespeichert sind. Die zurückgegebenen Ansichten enthalten nur Daten und kein HTML-Markup, wie beispielsweise XML- oder JSON-Feeds;
  • in [2]: Der Browser zeigt statische Ansichten (d. h. nicht dynamisch generierte) an, die von einem Webserver bereitgestellt werden, der sich auf demselben Rechner wie Server [1] befinden kann oder auch nicht. Diese statischen Ansichten werden dann mit Daten angereichert, die JavaScript über die Webschnittstelle [1] abruft;
  • Der in die HTML-Seiten eingebettete JavaScript-Code kann in Schichten strukturiert werden:
    • Die [Präsentations-]Ebene verarbeitet Benutzerinteraktionen,
    • die [DAO]-Schicht übernimmt den Datenzugriff über den Webserver [1],
    • die [Geschäftslogik]-Schicht entspricht der [Geschäftslogik]-Schicht, die sich zuvor auf dem Server [1] befand und in den Browser [2] verlagert wurde;

Der Vorteil dieser Architektur besteht darin, dass sie auf unterschiedliche Kompetenzbereiche zurückgreift:

  • Der Code des Webservers [1] erfordert .NET-Kenntnisse, jedoch keine Kenntnisse in JavaScript, HTML oder CSS;
  • Der im Browser [2] eingebettete Code 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 eine Anomalie im Beispiel Ajax-01 erwähnt. Hier eine Zusammenfassung des Kontexts:

  • In [1] und [2] wurden ungültige Werte eingegeben. Diese werden von den clientseitigen Validatoren markiert;
  • in [3] haben wir auf den Link [Berechnen] geklickt;
  • in [4] erfolgte ein [POST], da wir die Antwort [4] erhalten haben.

Wenn die Werte ungültig sind und die Schaltfläche [Berechnen] angeklickt wird, erfolgt keine [POST]-Anfrage an den Server. Im gleichen Szenario löst das Anklicken des Links [Berechnen] die [POST]-Anfrage an den Server aus. Daher gibt es ein Verhalten der Schaltfläche [Berechnen], das wir mit dem Link [Berechnen] nicht reproduzieren konnten.

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

7.6.1. Die Ansichten im Beispiel

Das Beispiel enthält mehrere Ansichten:

  • in [1] die Ansicht [Action05Get];
  • in [2] die Teilansicht [Form05];
  • bei [3] die Teilansicht [Failure05];

  • in [4] die Teilansicht [Success05].

Die Anwendung ist eine Single-Page-Anwendung: Die Seite wird vom Browser bei der ersten Anfrage geladen. Anschließend wird sie über Ajax-Aufrufe aktualisiert.

Die vorangehenden Seiten werden durch die folgenden [cshtml]-Ansichten generiert:

Die zunächst geladene Ansicht ist die folgende [Action05Get.cshtml]-Ansicht:


@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 folgende Punkte:

  • Zeile 1: Das View-Modell ist vom Typ [ViewModel05], auf den wir gleich noch eingehen werden;
  • Zeilen 13–19: Diese enthalten die für Ajax und die clientseitige Validierung erforderlichen JavaScript-Skripte;
  • Zeile 20: Wir werden unsere eigenen JavaScript-Funktionen in [myScripts-05.js] hinzufügen;
  • Zeile 27: das animierte Lade-Bild;
  • Zeilen 28–30: ein `id`-Tag [content]. Hier werden die Teilansichten [Form05, Success05, Failure05] eingefügt;
  • Zeile 29: Einfügen der Teilansicht [Form05].

Die Ansicht [Action05Get] ist für die Anzeige von Abschnitt [1] der Startseite zuständig:

Die Teilansicht [Formulaire05] generiert den obigen Abschnitt [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 einen Typ [ViewModel05] als Modell;
  • Zeile 3: das durch die Methode [Html.BeginForm] generierte Formular. Da dieses Formular über einen Ajax-Aufruf übermittelt 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 [form];
  • 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 alle zugehörigen Fehlermeldungen löscht.

Beachten Sie, dass das Formular keine [Submit]-Schaltfläche hat. Wir müssen die eingegebenen Werte von A und B manuell [Post]en.

Wenn keine Fehler vorliegen, werden die Ergebnisse angezeigt:

Abschnitt [4] oben 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 ein Modell vom Typ [ViewModel05];
  • Zeile 12: Ein JavaScript-Link, um zu den Eingabefeldern zurückzukehren.

Im Fehlerfall wird eine andere Teilansicht [3] angezeigt:

Diese Ansicht wird durch den folgenden [Failure05.cshtml]-Code 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 ein Modell vom Typ [ViewModel05];
  • Zeile 14: Ein JavaScript-Link, um zu den Eingabefeldern zurückzukehren.

7.6.2. Das View-Modell

Alle vorherigen Ansichten nutzen dasselbe Modell [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
  {
    // form
    [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; }
 
    // results
    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; }
  }
}

Dies ist das bereits vorgestellte Modell [ViewModel01] mit einigen geringfügigen Änderungen:

  • Zeilen 15 und 19: Die Felder A und B sind nun vom Typ [string], sodass bei der ersten Anzeige des Eingabeformulars leere Eingabefelder anstelle von Feldern mit dem Wert 0 angezeigt werden;
  • Zeilen 14 und 18: Dies verhindert nicht, dass der eingegebene Wert mit einem [Range]-Validator überprüft wird;
  • Zeile 26: eine Liste von Fehlermeldungen, die von der Ansicht [Failure05] angezeigt werden.

7.6.3. Daten im [Session]-Bereich

In Abschnitt 7.3.6 haben wir gesehen, dass Sitzungsdaten im folgenden [SessionModel]-Modell gekapselt waren:


using System;
namespace Exemple_03.Models
{
  public class SessionModel
  {
    // the random number generator
    public Random Randomizer { get; set; }
  }
}

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


using System;
namespace Exemple_03.Models
{
  public class SessionModel
  {
    // the random number generator
    public Random Randomizer { get; set; }
    // the values of A and 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 in der folgenden Sequenz gezeigt:

Anfrage 1

Anfrage 2

In [4] sehen wir die in [1] vorgenommenen Einträge. Es gibt jedoch zwei unterschiedliche HTTP-Anfragen. Wir wissen, dass die Sitzung als Speicher zwischen zwei HTTP-Anfragen dient. Damit die zweite Anfrage die von der ersten gesendeten Werte abrufen kann, müssen diese Werte in der Sitzung gespeichert sein.

7.6.4. Die Serveraktion [Action05Get]

Die Aktion [Action05Get] ist die Aktion, die die erste einzelne Seite 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 Ansicht [Action05Get.cshtml], die wir bereits besprochen haben, wird mit einem Modell vom Typ [ViewModel05] angezeigt;

7.6.5. Die Client-Aktion [Calculate]

Betrachten wir nun die Interaktionen des Benutzers mit den Ansichten:

Link [1] ist ein JavaScript-Link:


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

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


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

Der Code für die JavaScript-Funktion [calculate] lautet wie folgt:


// global data
var content;
var loading;
 
function calculer() {
  // first the references on DOM
  var formulaire = $("#formulaire");
  // then validate the form
  if (!formulaire.validate().form()) {
    // invalid form - terminated
    return;
  }
  // make a manual Ajax call
  $.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) {
      // display server response
      content.html(jqXHR.responseText);
    }
  })
}
 
function retourSaisies() {
 ...
}
 
function effacer() {
  ...
}
 
// document loading
$(document).ready(function () {
  // retrieve the references of the page's various components
  loading = $("#loading");
  content = $("#content");
  // we hide the moving image
  loading.hide();
});
  • Beachten Sie, dass JavaScript-Code immer auf der Client-Seite, also im Browser, ausgeführt wird;
  • Zeile 44: Die JS-Funktion, die ausgeführt wird, wenn das anfängliche Laden der einzelnen Seite 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 [Form05, Success05, Failure05];
  • Zeilen 2–3: Die Variablen aus den Zeilen 46–47 werden als global 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 [calculate];
  • Zeile 7: Es wird eine Referenz auf das Formular abgerufen. Die Teilansicht [Form05] hat ihm die ID [form] zugewiesen;
  • Zeile 9: Diese Anweisung führt die clientseitigen Formularvalidatoren aus. Dies war das, was in dem auf Seite 176 identifizierten Problem fehlte. Diese Methode wird von der Bibliothek [jquery.unobtrusive-ajax] bereitgestellt, die von der Single-Page-Anwendung 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 deklariert wird;

  • Zeile 11: Der Ajax-Aufruf an den Server wird nicht ausgeführt, wenn das Formular ungültig ist;
  • Zeilen 14–32: Der Ajax-Aufruf an den Server wird ausgeführt;
  • Zeile 15: Die Ziel-URL ist die Serveraktion [Action05FaireCalcul];
  • Zeile 16: Die Anfrage erfolgt über einen [POST];
  • Zeile 17: die gesendeten Werte. Dies sind die Formulareinträge, in diesem Fall die Werte von A und B;
  • Zeilen 22–24: Wenn der Ajax-Aufruf erfolgreich ist, aktualisiert die Funktion [calculate] den Bereich mit der ID [content] mit dem vom Server gesendeten HTML-Stream.

Dieser HTML-Stream ist derjenige, der von der Aktion [Action05FaireCalcul] gesendet wird, auf die der Ajax-Aufruf abzielt. Der Code für diese serverseitige Aktion lautet wie folgt:


    [HttpPost]
    public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
    {
      // model
      ViewModel05 modèle = new ViewModel05();
      // calculation time
      modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // model update
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // returns an error
        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 die gesendeten Werte (postedData) und das Sitzungsmodell (session) als Parameter;
  • Zeile 5: Das Teilansichtsmodell wird erstellt;
  • Zeile 7: Es wird mit der Berechnungszeit aktualisiert;
  • Zeile 9: Wir versuchen, die übermittelten Werte auf das Modell anzuwenden. Dessen Validatoren werden dann ausgeführt. Man könnte sich fragen, warum wir uns diese Mühe machen, wenn clientseitige Validatoren den POST-Vorgang verhindern, wenn die eingegebenen Daten ungültig sind. Tatsächlich sind wir uns über die Herkunft des POST-Vorgangs nicht sicher. Er könnte von Code stammen, der nicht von uns stammt. Daher müssen wir immer serverseitige Validierungen durchführen;
  • Zeile 10: Wir prüfen, ob die Validatoren erfolgreich waren;
  • Zeile 13: Ist das Modell ungültig, aktualisieren wir es mit einer Liste von Fehlern. Wir werden nicht näher auf die interne Methode [getListOfMessagesFor] eingehen, die analog zur auf Seite 60 beschriebenen Methode [GetErrorMessagesFor] ist;
  • Zeile 14: Die Partial-Ansicht [Failure05] wird mit ihrem Modell angezeigt. Hier ist der Code für diese Ansicht;

@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 Liste der Formularfehler wird mithilfe eines <ul>-Tags angezeigt.

Zur Erinnerung: Die JS-Funktion [calculate], die die Aktion [Post] an den Server [Action05FaireCalcul] auslöst, platziert diese HTML-Ausgabe in dem Bereich mit der ID [content]. Das Ergebnis sieht in etwa so aus:

Sehen wir uns nun den Code für die Aktion [Action05FaireCalcul] genauer an:


    [HttpPost]
    public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
    {
      // model
      ViewModel05 modèle = new ViewModel05();
...
      // we put the values of A and B in session
      session.A = modèle.A;
      session.B = modèle.B;
      // no errors so far
      List<string> erreurs = new List<string>();
      // every other time, an error is simulated
      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);
      }
      // calculations
      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);
      // view
      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 der Sitzung gespeichert. Wir möchten sie in der folgenden Abfrage abrufen können;
  • Zeilen 11–22: Es wird jedes zweite Mal zufällig ein Fehler generiert;
  • Zeilen 24–29: Wir führen die vier arithmetischen Operationen mit den eingegebenen reellen Zahlen durch;
  • Zeile 31: Die Teilansicht [Success05] wird mit ihrem Modell zurückgegeben. Diese Teilansicht 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>

Beachten Sie, dass die JS-Funktion [calculate], die die Aktion [Action05FaireCalcul] zum Senden an den Server auslöst, diesen HTML-Stream in den Bereich mit der ID [content] einfügt. Das Ergebnis sieht in etwa so aus:

7.6.6. Die Schaltfläche [Clear]

Der JavaScript-Link [Clear] setzt das Formular auf seinen Ausgangszustand zurück:

Image

Image

Im Formular ist der JS-Link [Löschen] wie folgt definiert:


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

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


// global data
var content;
var loading;
 
function calculer() {
...
}
 
function retourSaisies() {
...
}
 
function effacer() {
  // first the references on DOM
  var formulaire = $("#formulaire");
  var A = $("#A");
  var B = $("#B");
  // valid values are assigned to entries
  A.val("0");
  B.val("0");
  // then validate the form to make
  // any error msg
  formulaire.validate().form();
  // then assign empty strings to the input fields
  A.val("");
  B.val("");
}
 
// document loading
$(document).ready(function () {
  // retrieve the references of the page's various components
  loading = $("#loading");
  content = $("#content");
  // we hide the moving image
  loading.hide();
});
  • Zeilen 15–17: Abrufen von Verweisen auf verschiedene Elemente des DOM (Document Object Model);
  • Zeilen 19–20: Wir setzen gültige Werte in die Eingabefelder für die Zahlen A und B;
  • Zeile 23: Ausführung der clientseitigen Validatoren. Da die Werte von A und B gültig sind, werden dadurch eventuell angezeigte Fehlermeldungen entfernt;
  • Zeilen 25–26: In die Eingabefelder für die Zahlen A und B werden leere Zeichenfolgen eingegeben;

7.6.7. Die clientseitige Aktion [Zurück zu den Eingabefeldern]

Über den JavaScript-Link [Zurück zu den Eingabefeldern] können Sie nach dem Abrufen der Ergebnisse zum Formular zurückkehren:

Image

Image

Im Formular ist der JS-Link [Zurück zur Eingabe] wie folgt definiert:


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

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


// global data
var content;
var loading;
 
function calculer() {
...
}
 
function retourSaisies() {
  // make a manual Ajax call
  $.ajax({
    url: '/Premier/Action05RetourSaisies',
    type: 'POST',
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
      // IMPORTANT!!! validation
      $.validator.unobtrusive.parse($("#formulaire"));
    },
    error: function (jqXHR) {
      content.html(jqXHR.responseText);
    }
  })
}
 
function effacer() {
...
}
 
// document loading
$(document).ready(function () {
  // retrieve the references of the page's various components
  loading = $("#loading");
  content = $("#content");
  // we hide the moving image
  loading.hide();
});

  • Zeilen 11–29: ein Ajax-Aufruf;
  • Zeile 12: die Ziel-URL;
  • Zeile 13: Die Anfrage erfolgt über eine HTTP-POST-Anfrage. Dies ist eine POST-Anfrage ohne Parameter. Deshalb gibt es keine Zeile wie:
    data: formulaire.serialize(),

im Ajax-Aufruf;

  • Zeile 14: Die erwartete Antwort vom Server ist ein HTML-Stream;
  • Zeilen 18–20: Dieser HTML-Stream wird verwendet, um den Bereich mit der ID [content] zu aktualisieren;

Die Serveraktion [Action05RetourSaisies] lautet wie folgt:


    [HttpPost]
    public PartialViewResult Action05RetourSaisies(SessionModel session)
    {
      // view
      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 von A und B gespeichert haben;
  • Zeile 5: Wir geben die partielle Ansicht [Form05] mit einem Modell vom Typ [ViewModel05] zurück, wobei wir darauf achten, die Felder A und B mit den aus der Sitzung entnommenen Werten von A und B zu initialisieren;

Kehren wir nun zum Code für die JavaScript-Funktion [returnInput] zurück:


function retourSaisies() {
  // make a manual Ajax call
  $.ajax({
    url: '/Premier/Action05RetourSaisies',
    type: 'POST',
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
      // IMPORTANT!!! validation
      $.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 Lade-Bild wird ausgeblendet;
  • Zeile 16: eine etwas kryptische Anweisung, die ich online gefunden habe, um das folgende Problem zu lösen: In dem Formular, das über den Link [Zurück zur Eingabe] angezeigt wird, funktionierten die clientseitigen Validatoren nicht mehr. Bei der Suche nach Informationen zur JS-Bibliothek [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 ASP.NET-Anwendung im Internet zugänglich machen

Siehe Abschnitt 9.26.

7.8. Erstellen einer nativen Android-Anwendung aus einer Single-Page-APU-Anwendung

Siehe Abschnitt 9.27.