Skip to content

7. Ajaxificatie van een applicatie ASP.NET MVC

7.1. De rol van AJAX in een webapplicatie

Op dit moment hebben de bestudeerde leervoorbeelden de volgende architectuur:

Om van een weergave [Vue1] naar een weergave [Vue2] te gaan, doet de browser het volgende:

  • een verzoek naar de webapplicatie;
  • ontvangt de weergave [Vue2] en geeft deze weer in plaats van de weergave [Vue1].

Dit is het klassieke patroon:

  • verzoek van de browser;
  • de webserver stelt een weergave op als antwoord voor de klant;
  • weergave van deze nieuwe weergave door de browser.

Er bestaat nog een andere manier van interactie tussen de browser en de webserver: AJAX (Asynchronous JavaScript and XML). Het gaat hier in feite om interacties tussen de door de browser weergegeven weergave en de webserver. De browser blijft doen wat hij altijd doet, namelijk een weergave HTML weergeven, maar wordt nu aangestuurd door JavaScript dat is ingebed in de weergegeven weergave HTML. Het schema ziet er als volgt uit:

  • in [1] vindt er een gebeurtenis plaats op de pagina die in de browser wordt weergegeven (klik op een knop, wijziging van tekst, ...). Deze gebeurtenis wordt onderschept door JavaScript (JS) dat in de pagina is ingebed;
  • in [2] voert de JavaScript-code een verzoek uit (HTTP), net zoals de browser dat zou hebben gedaan. Het verzoek is asynchroon: de gebruiker kan blijven interageren met de pagina zonder te worden geblokkeerd door het wachten op het antwoord op het verzoek HTTP. Het verzoek doorloopt het gebruikelijke verwerkingsproces. Er is geen (of nauwelijks) verschil met een gewoon verzoek;
  • in [3] wordt een antwoord naar de client JS verzonden. In plaats van een volledige weergave HTML wordt eerder een gedeeltelijke weergave HTML, een stream XML of JSON (JavaScript Object Notation) die wordt verzonden;
  • in [4] haalt het JavaScript dit antwoord op en gebruikt het om een deel van de weergegeven pagina HTML bij te werken.

Voor de gebruiker is er sprake van een verandering in de weergave, omdat wat hij ziet is veranderd. Er vindt echter geen volledige herlaadbeurt van de pagina plaats, maar slechts een gedeeltelijke wijziging van de weergegeven pagina. Dit draagt bij aan de vloeiendheid en interactiviteit van de pagina: omdat de pagina niet volledig opnieuw wordt geladen, kunnen we gebeurtenissen verwerken die voorheen niet mogelijk waren. Bijvoorbeeld door de gebruiker een lijst met opties aan te bieden terwijl hij tekens invoert in een invoerveld. Bij elk nieuw ingevoerd teken wordt een verzoek AJAX naar de server gestuurd, die vervolgens nieuwe suggesties terugstuurt. Zonder Ajax was dit soort invoerhulp voorheen onmogelijk. Het was niet mogelijk om bij elk ingevoerd teken een nieuwe pagina te laden.

7.2. Basisbegrippen van JQuery en JavaScript

We hebben de JavaScript-bibliotheek JQuery vaak aan onze pagina’s toegevoegd. Daarin staat bijvoorbeeld de volgende regel:


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

Opmerking: pas de versie van jQuery aan aan die van uw versie van Visual Studio.

De Ajax-technologie van ASP.NET MVC maakt gebruik van JQuery. We gaan zelf enkele JQuery-scripts schrijven. Daarom bespreken we nu de basisbegrippen van JQuery die u moet kennen om de scripts in dit hoofdstuk te begrijpen.

We maken een nieuw project [Exemple-04] aan binnen onze oplossing [Exemples]:

Om Ajax te kunnen gebruiken met ASP.NET MVC, moet er een regel aanwezig zijn in het configuratiebestand [Web.config] [1]:


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

Regel 3 staat het gebruik van Ajax toe in de weergaven ASP.NET. Deze regel is standaard aanwezig.

We maken een bestand HTML [JQuery-01.html] aan in de map [Content] van het nieuwe project [2]:

Dit bestand heeft de volgende inhoud:


<!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>
  • regel 6: import van JQuery (pas de versie aan aan die van uw versie van Visual Studio);
  • regels 10-12: een pagina-element met id [element1]. We gaan met dit element aan de slag.

We bekijken dit bestand in de Google Chrome-browser: [4] en [5]:

Gebruik in Google Chrome de toetsencombinatie [Ctrl-Maj-I] om de ontwikkeltools [6] te openen. Via het tabblad [Console] [7] kun je JavaScript-code uitvoeren. Hieronder geven we enkele JavaScript-opdrachten die je kunt invoeren, samen met een uitleg.

JS
resultaat
$("#element1")
: geeft de verzameling van alle elementen met de id [element1] weer, dus normaal gesproken een verzameling van 0 of 1 element, omdat er op een pagina HTML geen twee identieke id’s kunnen voorkomen.
$("#element1").text("blabla")
: past de tekst [blabla] toe op alle elementen van de collectie. Dit heeft tot gevolg dat de inhoud die door de pagina wordt weergegeven, verandert
$("#element1").hide()
verbergt de elementen van de collectie. De tekst [blabla] wordt niet meer weergegeven.
$("#element1")
: toont de collectie weer. Zo kunnen we zien dat het element met id [element1] het attribuut CSS style='display: none;' heeft, waardoor het element verborgen is.
$("#element1").show()
: toont de elementen van de verzameling. De tekst [blabla] verschijnt opnieuw. Dit wordt mogelijk gemaakt door het attribuut CSS style='display: block;'.
$("#element1").attr('style','color: red')
: wijst een attribuut toe aan alle elementen van de verzameling. Het attribuut is hier [style] en de waarde ervan is [color: red]. De tekst [blabla] wordt rood weergegeven.
Tableau
Dictionnaire

Merk op dat de URL van de browser tijdens al deze bewerkingen niet is veranderd. Er heeft geen communicatie met de webserver plaatsgevonden. Alles gebeurt binnen de browser. Laten we nu de broncode van de pagina bekijken:

 

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

Dit is de oorspronkelijke tekst. Deze geeft op geen enkele manier de bewerkingen weer die we op het element in de regels 10-12 hebben uitgevoerd. Het is belangrijk om dit in gedachten te houden bij het debuggen van JavaScript. Het is dan vaak zinloos om de broncode van de weergegeven pagina te bekijken. Om de broncode van de momenteel weergegeven pagina te achterhalen, gaan we als volgt te werk:

 

We weten nu genoeg om de volgende JS-scripts te begrijpen.

7.3. Een pagina bijwerken met een feed HTML

7.3.1. De weergaven

We gaan de volgende toepassing bestuderen:

  • in [1], het tijdstip waarop de pagina wordt geladen;
  • in [2] worden de vier rekenkundige bewerkingen uitgevoerd op twee reële getallen A en B;
  • in [3] wordt het antwoord van de server in een deel van de pagina weergegeven;
  • in [4], het tijdstip van de berekening. Dit verschilt van het tijdstip waarop de pagina is geladen, [5]. Dit laatste is gelijk aan [1], wat aantoont dat het gebied [6] niet opnieuw is geladen. Bovendien is de URL [7] van de pagina niet veranderd.

7.3.2. De controller, de acties, het model, de view

We maken een controller met de naam [Premier]:

Om de startweergave weer te geven, maken we de volgende actie [Action01Get] aan:


    [HttpGet]
    public ViewResult Action01Get()
    {
      ViewModel01 modèle = new ViewModel01();
      modèle.HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      return View(modèle);
}
  • regel 4: het model van de weergave instantiëren;
  • regel 5: initialisatie van het laadtijdstip van de weergave;
  • regel 6: weergave van de weergave [Action10Get.cshtml] en het bijbehorende model.

Het model [ViewModel01] is als volgt:


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
  {
    // formulier
    [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; }

    // resultaten
    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; }
  }
}
  • regels 11-14: de waarde A van het formulier;
  • regels 15-18: de waarde B van het formulier;
  • regels 21-24: de resultaten van de vier rekenkundige bewerkingen op A en B;
  • regel 25: de tekst van een eventuele foutmelding;
  • regel 26: het tijdstip waarop de weergave in de browser is geladen;
  • regel 27: het tijdstip waarop de velden van de regels 21-24 zijn berekend;
  • regel 7: dit weergavesjabloon is tevens een actiesjabloon. Hieruit worden de velden weggelaten die niet door de browser worden verzonden.

De weergave [Action01Get.cshtml] ziet er als volgt uit:


@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>
  • regel 1: de weergave is gebaseerd op het type [ViewModel01];
  • regel 21: JQuery is nodig voor zowel validaties als Ajax;
  • regels 22-23: de validatiebibliotheken;
  • regels 24-26: de bibliotheken voor internationalisering;
  • regel 27: de Ajax-bibliotheek;
  • regel 28: een lokale JavaScript-bibliotheek;
  • regel 33: weergave van de laadtijd van de weergave;
  • regel 35: een Ajax-formulier – hier komen we later op terug;
  • regels 40-41: labels voor de invoervelden van de getallen A en B;
  • regels 46-47: invoervelden voor de getallen A en B;
  • regels 50-51: foutmeldingen voor de invoer van de getallen A en B;
  • regel 56: de knop die het formulier verstuurt. Dit wordt verzonden via een Ajax-verzoek;
  • regel 57: een laadafbeelding die wordt weergegeven tijdens het Ajax-verzoek;
  • regel 58: een link om het formulier via een Ajax-verzoek te verzenden;
  • regel 62: een <div>-tag met de id [résultats]. Hier plaatsen we de feed HTML die door de webserver wordt teruggestuurd.

Deze weergave toont de volgende pagina:

 

Laten we nu eens kijken naar de code die het formulier via Ajax verwerkt:


...
@{
  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" />
  • regel 15: in plaats van [@Html.BeginForm] wordt [@Ajax.BeginForm] gebruikt. Deze methode kent talrijke overladingen. De gebruikte methode heeft de volgende signatuur:
Ajax.BeginForm(string ActionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string,object> htmlAttributes)

We gebruiken hier de volgende daadwerkelijke parameters:

Action01Post: de naam van de actie die het POST van het formulier zal verwerken,

null: er hoeft geen route-informatie te worden opgegeven,

ajaxOpts: de opties van de Ajax-aanroep. Deze zijn gedefinieerd in de regels 6-10,

new { id = "formulaire" }: om het attribuut [id='formulaire'] toe te wijzen aan de gegenereerde form-tag;

De gebruikte Ajax-opties zijn als volgt:

  • regel 8: het doel van de Ajax-aanvraag;
  • regel 7: methode van het Ajax-verzoek HTTP;
  • regel 6: id van het gedeelte van de pagina dat door het antwoord op het Ajax-verzoek wordt bijgewerkt;
  • regel 9: id van het gedeelte van de pagina dat tijdens de Ajax-verzoek wordt weergegeven – meestal een laadafbeelding. Hier wordt regel 20 weergegeven. Deze bevat een geanimeerde afbeelding die het wachten symboliseert. In eerste instantie wordt deze afbeelding verborgen door de stijl [display : none];
  • regel 10: wachttijd in milliseconden voordat de geanimeerde afbeelding wordt weergegeven, hier 1 seconde.

De door het Ajax-formulier gegenereerde code HTML is als volgt:


<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" />
  • regel 1: de gegenereerde <form>-tag. Let op de attributen [data-ajax-attr], die de waarden weergeven van de velden van het object van het type [AjaxOptions] dat aan het Ajax-verzoek is gekoppeld. Deze attributen worden beheerd door de Ajax-bibliotheek. Zonder deze attributen wordt de <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>

We hebben dan te maken met een klassiek HTML-formulier. Deze code wordt uitgevoerd als de gebruiker JavaScript in zijn browser uitschakelt. De regels 5-6 worden dan niet gebruikt.

7.3.3. De actie [Action01Post]

De actie [Action01Post], die het Ajax-verzoek HTTP verwerkt, ziet er als volgt uit:


    [HttpPost]
    public PartialViewResult Action01Post(FormCollection postedData, SessionModel session)
    {
      // verwachtingssimulatie
      Thread.Sleep(2000);
      // instantie van het actiemodel
      ViewModel01 modèle = new ViewModel01();
      // berekeningstijd
      modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // modelupdate
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // er wordt een fout geretourneerd
        modèle.Erreur = getErrorMessagesFor(ModelState);
        return PartialView("Action01Error", modèle);
      }
      // om de andere keer wordt een fout gesimuleerd
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        modèle.Erreur = "[erreur aléatoire]";
        return PartialView("Action01Error", modèle);
      }
      // berekeningen
      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);
      // weergave
      return PartialView("Action01Success", modèle);
}
  • regel 1: de actie verwerkt slechts één [POST];
  • regel 2: het accepteert als actiemodel:
    • [FormCollection postedData]: alle waarden die door het Ajax-verzoek POST zijn verzonden,
    • [SessionModel session]: de sessie-elementen. Hier wordt gebruikgemaakt van een techniek die in paragraaf 4.10 wordt beschreven;
  • regel 2: de actie retourneert een fragment HTML en geen volledige pagina HTML;
  • regel 5: we pauzeren kunstmatig twee seconden om een langdurige Ajax-actie te simuleren;
  • regel 7: er wordt een model van het type [ViewModel01] geïnstantieerd;
  • regel 9: de berekeningstijd wordt geïnitialiseerd;
  • regel 11: er wordt geprobeerd het model van het type [ViewModel01] bij te werken met de verzonden waarden. Ter herinnering: er zijn er twee: de waarden van de getallen A en B;
  • regel 12: er wordt gecontroleerd of deze update is geslaagd;
  • regel 15: in geval van een fout wordt het veld [Erreur] van het model ingevuld;
  • regel 16: er wordt een gedeeltelijke weergave [Action01Error.cshtml] geretourneerd op basis van het model [ViewModel01];
  • regels 19-24: om de andere keer wordt een fout gesimuleerd;
  • regel 19: er wordt een willekeurig geheel getal gegenereerd in het interval [0,1]. De getallengenerator wordt uit de sessie gehaald;
  • regel 20: als de gegenereerde waarde 0 is, wordt er een fout gesimuleerd;
  • regel 22: de foutmelding wordt in het model geplaatst;
  • regel 23: er wordt een deelweergave [Action01Error.cshtml] gerenderd op basis van het model [ViewModel01];
  • regels 26-29: de rekenkundige bewerkingen op de getallen A en B worden uitgevoerd en de resultaten worden in de vorm van tekenreeksen in het model geplaatst;
  • regel 31: er wordt een deelweergave [Action01Success.cshtml] gerenderd op basis van het model [ViewModel01];

7.3.4. De weergave [Action01Error]

De weergave [Action01Error.cshtml] ziet er als volgt uit:


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

Ter herinnering: deze gedeeltelijke feed HTML wordt verzonden als antwoord op het Ajax-verzoek HTTP van het type POST en wordt op de pagina geplaatst in het gebied met id [résultats]. Al deze informatie is afkomstig uit de Ajax-configuratie die wordt gebruikt op de hoofdpagina [Action01Get.cshtml]:


@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 volgt een voorbeeld van een antwoord met een fout:

 

7.3.5. De weergave [Action01Success]

De weergave [Action01Success.cshtml] ziet er als volgt uit:


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

Ook hier zal deze gedeeltelijke feed HTML worden verzonden als antwoord op het Ajax-verzoek HTTP van het type POST en op de pagina worden geplaatst in het gebied met id [résultats]:

 

7.3.6. G essiebeheer

We hebben gezien dat [Action01Post] gebruikmaakt van de sessie. Het sessiemodel is het volgende type [SessionModel]:


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

De sessie wordt geïnitialiseerd in [Global.asax]:


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

De koppeling van de sessie aan een model vindt plaats in [Application_Start]:


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

De klasse [SessionModelBinder] is beschreven.

7.3.7. Beheer van het wachtbeeld


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

Wanneer het Ajax-verzoek start, wordt het gebied met id [loading] op regel 7 na één seconde weergegeven [ligne 8]. Dit gebied is de afbeelding van regel 21 die aanvankelijk verborgen was. Dit levert de volgende interface op:

Laten we de link [Calculer] op de hoofdpagina [Action01Get.cshtml] eens bekijken:


<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" />
  • regel 18: een klik op de link [Calculer] zorgt ervoor dat de functie JS [postForm] wordt uitgevoerd. Deze is gedefinieerd in het bestand [myScripts-01.js] op regel 5. Dit script luidt als volgt:

function postForm() {
  // er wordt handmatig een Ajax-aanroep gedaan met JQuery
  var loading = $("#loading");
  var formulaire = $("#formulaire");
  var résultats = $('#resultaten');
  $.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) {
    //Gebruik de Globalization-plugin om de waarde te parseren        
    var val = Globalize.parseFloat(value);
    return this.optional(element) || (
        val >= param[0] && val <= param[1]);
  }
});

De functies op de regels 19-37 zijn al aan bod gekomen in paragraaf 6.1. Ze zorgen voor de internationalisering van de pagina's. We gaan hier niet verder op in. In de regels 1-17 voeren we handmatig de Ajax-aanroep uit, die in het geval van de knop [Calculer] werd uitgevoerd door de aan het project gekoppelde Ajax-bibliotheek. Hiervoor gebruiken we de aan het project gekoppelde bibliotheek JQuery.

  • regel 3: een verwijzing naar de component met id [loading]. [$("#loading")] retourneert de verzameling elementen met id [loading]. Er is er maar één;
  • regel 4: een verwijzing naar de component met id [formulaire];
  • regel 5: een verwijzing naar de component met id [résultats];
  • regel 6: de Ajax-aanroep met de bijbehorende opties;
  • regel 7: het doel van de Ajax-aanroep (URL);
  • regel 8: de gebruikte methode HTTP;
  • regel 9: de verzonden gegevens. [formulaire.serialize] genereert de tekenreeks [A=val1&B=val2] van het veld met id [formulaire];
  • regel 10: het type gegevens dat als retourwaarde wordt verwacht. We weten dat de server een stream met de naam HTML zal terugsturen;
  • regel 11: de methode die moet worden uitgevoerd wanneer het verzoek start. Hier wordt aangegeven dat de component met id [loading] moet worden weergegeven. Dit is de geanimeerde laadafbeelding;
  • regel 12: de methode die moet worden uitgevoerd als de Ajax-verzoek succesvol is. De parameter [data] is het volledige antwoord van de server. We weten dat dit een stream is met de waarde HTML;
  • regel 13: het wachtsignaal wordt in de cache opgeslagen;
  • regel 14: de component met id [résultats] wordt bijgewerkt met de waarde HTML uit de parameter [data].

De lezer wordt uitgenodigd om de link [Calculer] te testen. Deze werkt net als de knop [Calculer], op één afwijkende na. Zodra deze link is gebruikt, kunnen er ongeldige waarden voor A en B worden verzonden:

  • in [1] en [2] zijn ongeldige waarden ingevoerd. Deze worden gemeld door de validators aan de clientzijde;
  • bij [3] is op de link [Calculer] geklikt;
  • in [4] is er een [POST] geweest, aangezien we het antwoord [4] krijgen.

Wanneer de waarden ongeldig zijn en men op de knop [Calculer] klikt, vindt de [POST] naar de server niet plaats. In hetzelfde geval vindt bij de link [Calculer] wel de [POST]-verzoek naar de server plaats. Er is dus een bepaald gedrag van de knop [Calculer] dat we niet hebben kunnen reproduceren met de link [Calculer]. In plaats van dit probleem nu op te lossen, laten we het staan voor een later voorbeeld dat ook een ander probleem met validatie aan de clientzijde zal illustreren.

7.4. Een pagina HTML bijwerken met een feed JSON

In het vorige voorbeeld reageerde de webserver op het Ajax-verzoek HTTP met een feed HTML. In deze feed stonden gegevens vergezeld van opmaak HTML. We stellen voor om het vorige voorbeeld te herhalen, maar ditmaal met JSON-antwoorden (JavaScript Object Notation) die alleen de gegevens bevatten. Het voordeel hiervan is dat er zo minder bytes worden verzonden.

7.4.1. De actie [Action02Get]

De actie [Action02Get] vormt het startpunt van de nieuwe toepassing. De code ervan is als volgt:


@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>
  • regels 4-14: de opties van de Ajax-aanroep;
  • regel 10: de functie JS die bij het starten van de aanvraag moet worden uitgevoerd. Deze functie is gedefinieerd in het bestand JS waarnaar op regel 24 wordt verwezen;
  • regel 11: de functie JS die moet worden uitgevoerd als de aanvraag mislukt;
  • regel 12: de functie JS die moet worden uitgevoerd als de aanvraag slaagt;
  • regel 13: de functie JS die moet worden uitgevoerd nadat de Ajax-aanvraag een resultaat heeft opgeleverd (mislukt of geslaagd);
  • regels 40-43: een regio met id [entete];
  • regels 44-49: een regio met id [résultats]. Deze geeft de resultaten van de vier rekenkundige bewerkingen weer;
  • regels 50-52: een regio met id [erreur]. Hierin wordt een eventuele foutmelding weergegeven.

7.4.2. De actie [Action02Post]

Het Ajax-verzoek wordt verwerkt door de volgende actie [Action02Post]:


[HttpPost]
    public JsonResult Action02Post(FormCollection postedData, SessionModel session)
    {
      // simulatie in behandeling
      Thread.Sleep(2000);
      // modelvalidatie
      ViewModel02 modèle = new ViewModel02();
      // laad- en rekentijd
      string HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      string HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // modelupdate
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // er wordt een fout geretourneerd
        return Json(new { Erreur = getErrorMessagesFor(ModelState), HeureCalcul = HeureCalcul });
      }
      // om de andere keer wordt er een fout gesimuleerd
      int val = session.Randomizer.Next(2);
      if (val == 0)
      {
        // er wordt een foutmelding teruggestuurd
        return Json(new { Erreur = "[erreur aléatoire]", HeureCalcul = HeureCalcul });
      }
      // berekeningen
      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);
      // de resultaten worden teruggestuurd
      return Json(new { Erreur = "", AplusB = AplusB, AmoinsB = AmoinsB, AmultipliéparB = AmultipliéparB, AdiviséparB = AdiviséparB, HeureCalcul = HeureCalcul });
    }
  • regel 2: de methode retourneert een type [JsonResult], d.w.z. tekst in het formaat JSON;
  • regel 16: de informatie wordt weergegeven in de vorm van een anonieme klasse-instantie die is geserialiseerd in JSON. De methode [getErrorMessagesFor] is al eerder besproken. De string JSON die naar de browser wordt verzonden, ziet er als volgt uit:
{"Erreur":"[erreur aléatoire]","HeureCalcul":"05:31:37"}
  • regel 31: dezelfde werkwijze voor rekenresultaten. Ditmaal zal de naar de browser verzonden tekenreeks JSON de volgende vorm hebben:
{"Erreur":"","AplusB":"4","AmoinsB":"-2","AmultipliéparB":"3","AdiviséparB":"0,333333333333333","HeureCalcul":"05:52:17"}

7.4.3. De JavaScript-code aan de clientzijde

Laten we nog eens kijken naar de configuratie van de Ajax-aanroep in de pagina HTML die naar de browser van de klant wordt verzonden:


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

De functies JS waarnaar in de regels 7-10 (rechts van het =-teken) wordt verwezen, zijn gedefinieerd in het volgende bestand [myScripts-02.js]:


// algemene gegevens
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() {
...
}

// bij het laden van het document
$(document).ready(function () {
  formulaire = $("#formulaire");
  entete = $("#entete");
  loading = $("#loading");
  erreur = $("#erreur");
  résultats = $('#resultaten');
  heureCalcul = $("#heureCalcul");
  msg = $("#msg");
  AplusB = $("#AplusB");
  AmoinsB = $("#AmoinsB");
  AmultipliéparB = $("#AmultipliéparB");
  AdiviséparB = $("#AdiviséparB");

  // bepaalde elementen van de pagina worden in de cache opgeslagen
  entete.hide();
  résultats.hide();
  erreur.hide();
});

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

// einde van de aanvraag
function OnComplete() {
...
}

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

// fout
function OnFailure(request, error) {
...
}
  • regel 19: de functie JS wordt uitgevoerd zodra de pagina volledig in de browser is geladen;
  • regels 20-30: we halen de referenties op van alle componenten op de pagina die voor ons van belang zijn. Het zoeken naar een component op een pagina kost tijd en het is beter om dit slechts één keer te doen;
  • regels 33-35: de componenten [entete], [résultats] en [loading] worden verborgen;

Bij het starten van de Ajax-aanvraag wordt de volgende functie uitgevoerd:


// start
function OnBegin() {
  // wachtlampje brandt
  loading.show();
  // bepaalde elementen van de pagina worden verborgen
  entete.hide();
  résultats.hide();
  erreur.hide();
}
  • regel 4: de component [loading] wordt weergegeven. Dit is de geanimeerde afbeelding;
  • regels 6-8: de componenten [entete], [résultats] en [erreur] worden verborgen;

Als het Ajax-verzoek slaagt, wordt de volgende code JS uitgevoerd:


// geslaagd
function OnSuccess(data) {
  // weergave resultaten
  heureCalcul.text(data.HeureCalcul);
  entete.show();
  if (data.Erreur != '') {
    msg.text(data.Erreur);
    erreur.show();
    return;
  }
  // geen fout
  AplusB.text(data.AplusB);
  AmoinsB.text(data.AmoinsB);
  AmultipliéparB.text(data.AmultipliéparB);
  AdiviséparB.text(data.AdiviséparB);
  résultats.show();
}

Om deze code te begrijpen, moet men de twee teksten JSON in gedachten houden die als antwoord naar de browser kunnen worden verzonden:

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

in geval van een fout, anders de tekenreeks:

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

Als we deze tekenreeks [data] noemen, wordt de waarde van het veld [Erreur] verkregen via de notatie [data.Erreur] of [data["Erreur"]], naar keuze. Hetzelfde geldt voor de andere velden van de tekenreeks JSON. Om overigens een niet-opgemaakte tekst toe te wijzen aan een component met id X, schrijft men [X.text(chaine)]. Laten we terugkeren naar de code van de functie [OnSuccess]:

  • regel 2: [data] is de ontvangen tekenreeks JSON;
  • regel 4: de component [heureCalcul] krijgt zijn waarde;
  • regel 5: de component [entete] wordt weergegeven;
  • regel 6: test van het veld [Erreur] van de tekenreeks JSON;
  • regel 7: de component [msg] krijgt zijn waarde;
  • regel 8: de component [erreur] wordt weergegeven;
  • regel 9: het is klaar voor het foutgeval;
  • regel 12: de component [AplusB] krijgt zijn waarde;
  • regel 13: de component [AmoinsB] krijgt zijn waarde;
  • regel 14: de component [AmultipliéparB] krijgt zijn waarde;
  • regel 15: de component [AdiviséparB] krijgt zijn waarde;
  • regel 16: de component [résultats] wordt weergegeven.

De functie [OnFailure] wordt uitgevoerd als de Ajax-aanvraag HTTP mislukt. Deze mislukking wordt gemeten aan de hand van de code HTTP die door de server wordt teruggestuurd. De code 500 [Internal Server Error] geeft bijvoorbeeld aan dat de server het verzoek niet heeft kunnen uitvoeren. De functie [OnFailure] is als volgt:


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

Er wordt alleen een dialoogvenster weergegeven met de fout die is opgetreden. In de praktijk zou men nauwkeuriger moeten zijn. We zullen binnenkort een andere oplossing voorstellen.

Ten slotte wordt de functie [OnComplete] uitgevoerd wanneer de aanvraag is voltooid, ongeacht of deze is geslaagd of mislukt.


// einde van de aanvraag
function OnComplete() {
  // wachtmelding uitgeschakeld
  loading.hide();
}

We herinneren er hier aan dat het de configuratie van de Ajax-aanroep in de weergave [Action02Get.cshtml] is die ervoor zorgt dat deze verschillende functies worden aangeroepen:


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

De code HTML van de link [Calculer] in de weergave [Action02Get.cshtml] is als volgt:


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

De functie JS [postForm] wordt aangetroffen in het geïmporteerde bestand [myScripts-02.js]:


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

De code ervan is als volgt:


function postForm() {
  // er wordt handmatig een Ajax-verzoek gedaan met JQuery
  $.ajax({
    url: '/Premier/Action02Post',
    type: 'POST',
    data: formulaire.serialize(),
    dataType: 'json',
    beforeSend: OnBegin,
    success: OnSuccess,
    error: OnFailure,
    complete: OnComplete
  })
}

We zijn al eens een vergelijkbare code tegengekomen.

  • regel 4: URL doel van de Ajax-aanroep;
  • regel 5: commando HTTP dat door de Ajax-aanroep wordt gebruikt;
  • regel 6: verzonden waarden. Deze zijn het resultaat van de serialisatie van de formulierwaarden. Dit formulier, aangeduid met de id [formulaire], wordt aangeduid door de variabele [formulaire]. [data] is een tekenreeks in de vorm [A=val1&B=val2];
  • regel 7: opmaaktype van het verwachte antwoord. Dit is een tekenreeks JSON;
  • regel 8: functie JS die bij het starten van de Ajax-aanroep moet worden uitgevoerd;
  • regel 9: functie JS die moet worden uitgevoerd als de Ajax-aanroep slaagt;
  • regel 10: functie JS die moet worden uitgevoerd als de Ajax-aanroep mislukt;
  • regel 11: functie JS, uit te voeren zodra het antwoord van de server is ontvangen, ongeacht of dit een succes of een fout is.

Laten we terugkomen op de JavaScript-functie die het geval afhandelt waarin de Ajax-aanroep mislukt (regel 10). De Ajax-aanroep mislukt in verschillende situaties, bijvoorbeeld wanneer de server een foutcode terugstuurt zoals [403 Forbidden], [404 Not Found], [500 Internal Server Error], [301 Moved Permanently], ...

In het vorige voorbeeld is de functie [OnFailure] als volgt:


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

Over het algemeen levert het weergeven van het object [error] geen interessante informatie op. Als er een Ajax-aanroep wordt gedaan met JQuery, kan de volgende methode [OnFailure] worden gebruikt;


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

Het object JQuery [jqXHR] heeft onder andere de volgende eigenschappen:

  • responseText: de tekst van het antwoord van de server;
  • status: de door de server geretourneerde foutcode;
  • statusText: de tekst die bij deze foutcode hoort.
  • regel 3: de foutcode en de bijbehorende tekst worden weergegeven;
  • regel 4: het serverantwoord HTML wordt in de component met id [msg] geplaatst;
  • regel 5: de regio met id [erreur] wordt weergegeven.

Om deze foutfunctie te testen, gaan we kunstmatig een uitzondering genereren in de actie [Action02Post]:


    [HttpPost]
    public JsonResult Action02Post(FormCollection postedData, SessionModel session)
    {
      // een kunstmatige uitzondering om de foutfunctie van de Ajax-aanroep te testen
      throw new Exception();
      // simulatie van wachttijd
      Thread.Sleep(2000);
      // modelvalidatie
...

Regel 5 genereert een uitzondering. Laten we nu de applicatie testen:

We krijgen de volgende reactie: [1] en [2]:

Aan de hand van het antwoord van de server kunnen we zien op welke regel van de servercode de fout is opgetreden. Dit is vaak nuttige informatie. Vanaf nu zullen we deze techniek gebruiken om fouten bij Ajax-aanroepen af te handelen.

7.5. Webapplicatie met één pagina

Met Ajax-technologie kunnen we applicaties met één pagina bouwen:

  • de eerste pagina wordt geladen via een klassieke browserverzoek;
  • de volgende pagina’s worden verkregen via Ajax-aanroepen. Uiteindelijk verandert de browser dus nooit van URL en laadt hij nooit een nieuwe pagina. Dit type applicatie wordt een Single Page Application (APU) genoemd, of in het Engels Single Page Application (SPA).

Hier volgt een eenvoudig voorbeeld van een dergelijke applicatie. De nieuwe applicatie zal twee weergaven hebben:

  • in [1] zorgt de actie [Action03Get] ervoor dat we de eerste pagina, pagina 1, te zien krijgen;
  • in [2] kunnen we via een link naar pagina 2 gaan dankzij een Ajax-aanroep;
  • in [3] is URL niet gewijzigd. De weergegeven pagina is pagina 2;
  • in [4] kunnen we via een link teruggaan naar pagina 1 dankzij een Ajax-aanroep;
  • in [5] is URL niet gewijzigd. De weergegeven pagina is pagina 1.

De code van de actie [Action03Get] is als volgt:


    [HttpGet]
    public ViewResult Action03Get()
    {
      return View();
}
  • regel 4: de weergave [Action03Get.cshtml] wordt weergegeven.

De weergave [Action03Get.cshtml] is als volgt:


@{
  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>
  • regels 16-18: een element met id [content]. In dit element worden de verschillende pagina's weergegeven;
  • regel 17: standaard wordt eerst de pagina [Page1.cshtml] weergegeven.

De pagina [Page1.cshtml] is de volgende:


<h4>Page 1</h4>
  <p>
    @Ajax.ActionLink("Page 2", "Action04", new { Page = 2 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>
  • regel 1: de titel van de pagina om deze te onderscheiden van pagina 2;
  • regel 3: een Ajax-link met de volgende parameters:
    • de tekst van de link [Page 2];
    • de doelactie van de link [Action04];
    • de parameters van de aangevraagde URL. Deze zal [/Premier/Action04?Page=2] zijn;
  • de opties van de Ajax-aanroep. Hier wordt alleen de id van de regio opgegeven die met het antwoord van de server moet worden bijgewerkt. Voor de overige opties worden standaardwaarden gebruikt, indien deze bestaan. De standaardmethode HTTP is GET.

Laten we eens kijken wat er gebeurt wanneer er op de link wordt geklikt. De URL [/Premier/Action04?Page=2] wordt aangevraagd met een GET. De actie [Action04] wordt vervolgens uitgevoerd:


    [HttpGet]
    public PartialViewResult Action04(string page = "1")
    {
      string vue = "Page1";
      if (page == "2")
      {
        vue = "Page2";
      }
      return PartialView(vue);
}
  • regel 2: de actie levert een gedeeltelijke HTML-stream op;
  • regel 2: de actie heeft de tekenreeks [page] als sjabloon. We weten echter dat URL deze informatie bevat: [/Premier/Action04?Page=2]. Ter herinnering: het sjabloon is hoofdletterongevoelig;
  • regels 4-8: [vue] krijgt de waarde [Page2];
  • regel 9: de deelweergave [Page2.cshtml] wordt weergegeven.

De deelweergave [Page2.cshtml] is als volgt:


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

De server stuurt dus de bovenstaande stream HTML terug als antwoord op de Ajax-aanroep GET [/Premier/Action04?Page=2]. We herinneren ons dat deze Ajax-aanroep dit antwoord gebruikt om het gebied met id [content] bij te werken (regel 3 hieronder):


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

Dit leidt tot de volgende nieuwe weergave: [1]:

Volgens dezelfde redenering zien we dat het klikken op de link [Page 1] vanuit [1] de weergave [2] zal veroorzaken.

Laten we terugkeren naar het algemene schema van een applicatie ASP.NET MVC:

Dankzij de JavaScript-code die in de pagina's HTML is ingebed en in de browser wordt uitgevoerd, kan code naar de browser worden verplaatst, wat resulteert in de volgende architectuur:

  • In [1] is de weblaag ASP.NET MVC een webinterface geworden voor toegang tot gegevens, die doorgaans in een database zijn opgeslagen. De weergegeven weergaven bevatten alleen gegevens en geen opmaak, bijvoorbeeld de feeds HTML, XML of JSON;
  • in [2]: de browser geeft statische weergaven weer (d.w.z. niet dynamisch gegenereerd) die worden geleverd door een webserver die al dan niet op dezelfde machine staat als de [1]-server. Deze statische weergaven worden vervolgens aangevuld met gegevens die via JavaScript worden opgehaald uit de webinterface [1];
  • de JavaScript-code die in de HTML-pagina's is ingebed, kan in lagen worden gestructureerd:
    • de laag [présentation] zorgt voor de interacties met de gebruiker,
    • de laag [DAO] zorgt voor de toegang tot de gegevens via de webserver [1],
    • de laag [métier] komt overeen met de laag [métier] die voorheen op de server [1] stond en die is verplaatst naar de browser [2];

Het voordeel van deze architectuur is dat er verschillende vaardigheden bij komen kijken:

  • de code van de webserver [1] vereist vaardigheden op het gebied van .NET, maar geen vaardigheden op het gebied van JavaScript, HTML of CSS;
  • de in de browser ingebedde code [2] vereist vaardigheden in JavaScript, HTML en CSS, maar is onafhankelijk van de technologie van de webserver [1].

Deze architectuur vergemakkelijkt dus het parallel werken van teams met verschillende vaardigheden. Ze is bijzonder geschikt voor Single-Page-applicaties.

7.6. Webapplicatie met één pagina en validatie aan de clientzijde

We hebben eerder een afwijking in het voorbeeld Ajax-01 genoemd. We herhalen de context:

  • in [1] en [2] zijn ongeldige waarden ingevoerd. Deze worden gesignaleerd door de validators aan de clientzijde;
  • in [3] is op de link [Calculer] geklikt;
  • in [4] is er een [POST] geweest, aangezien we het antwoord [4] krijgen.

Wanneer de waarden ongeldig zijn en men op de knop [Calculer] klikt, vindt de [POST] naar de server niet plaats. In hetzelfde geval vindt bij de link [Calculer] de [POST] naar de server wel plaats. Er is dus sprake van een bepaald gedrag van de knop [Calculer] dat we niet hebben kunnen reproduceren met de link [Calculer].

We zullen dit voorbeeld in een nieuwe context hernemen: de applicatie zal meerdere weergaven hebben en van het type [Application à Page Unique] zijn, zoals we zojuist hebben beschreven.

7.6.1. De weergaven van het voorbeeld

Het voorbeeld heeft verschillende weergaven:

  • in [1], de weergave [Action05Get];
  • in [2], de deelweergave [Formulaire05];
  • in [3], het deelbeeld [Failure05];
  • in [4], de deelweergave [Success05].

De applicatie bestaat uit één pagina: deze wordt bij het eerste verzoek door de browser geladen. Vervolgens wordt deze bijgewerkt via Ajax-aanroepen.

De voorgaande pagina’s worden gegenereerd door de volgende weergaven [cshtml]:

De weergave die in eerste instantie wordt geladen, is de volgende weergave [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>

Let op de volgende punten:

  • regel 1: het model van de weergave is van het type [ViewModel05], dat we straks zullen bespreken;
  • regels 13-19: hier staan de JavaScript-scripts die nodig zijn voor Ajax en validatie aan de clientzijde;
  • regel 20: we gaan onze eigen JavaScript-functies toevoegen in [myScripts-05.js];
  • regel 27: de geanimeerde afbeelding die wordt weergegeven tijdens het wachten;
  • regels 28-30: een tag met de id [content]. In deze tag worden de deelweergaven [Formulaire05, Success05, Failure05] ingevoegd;
  • regel 29: invoeging van de deelweergave [Formulaire05].

De weergave [Action05Get] is verantwoordelijk voor de weergave van het deel [1] van de startpagina:

De deelweergave [Formulaire05] genereert het bovenstaande gedeelte [2]. De code ervan is als volgt:


@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>
}
  • regel 1: de deelweergave accepteert als sjabloon een type [ViewModel05];
  • regel 3: het formulier dat wordt gegenereerd door de methode [Html.BeginForm]. Omdat dit formulier via een Ajax-aanroep wordt verzonden, worden de eerste drie parameters van de methode genegeerd. Tenzij de gebruiker JavaScript in zijn browser heeft uitgeschakeld. We gaan hier niet uit van deze mogelijkheid. De vierde parameter is belangrijk. Het formulier krijgt de id [formulaire];
  • regels 5-22: het formulier voor het invoeren van de getallen A en B;
  • regel 27: een JavaScript-link die de uitvoering van de vier rekenkundige bewerkingen op A en B start;
  • regel 30: een JavaScript-link die de invoer en eventuele bijbehorende foutmeldingen wist.

Merk op dat het formulier geen knop van het type [submit] heeft. We zullen de [Post] van de ingevoerde waarden A en B handmatig moeten uitvoeren.

Als er geen fouten zijn, worden de resultaten weergegeven:

Het bovenstaande gedeelte [4] wordt gegenereerd door de volgende deelweergave [Success05.cshtml]:


@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>
  • regel 1: de deelweergave [Success05.cshtml] ontvangt een sjabloon van het type [ViewModel05];
  • regel 12: een JavaScript-link om terug te keren naar de invoervelden.

Bij een fout wordt een andere deelweergave [3] weergegeven:

Deze weergave wordt gegenereerd door de volgende code [Failure05.cshtml]:


@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>
  • regel 1: het deeloverzicht [Failure05.cshtml] ontvangt een sjabloon van het type [ViewModel05];
  • regel 14: een JavaScript-link om terug te keren naar de invoervelden.

7.6.2. Het sjabloon voor de weergaven

Alle voorgaande weergaven delen hetzelfde sjabloon [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
  {
    // formulier
    [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; }

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

Dit is het model [ViewModel01] dat al eerder is gepresenteerd, op enkele details na:

  • regels 15 en 19: de velden A en B zijn nu van het type [string], zodat bij de eerste weergave van het invoerformulier lege invoervelden worden getoond in plaats van velden met de waarde 0;
  • regels 14 en 18: dit belet niet dat de ingevoerde waarde wordt gecontroleerd met een validator van het type [Range];
  • regel 26: een lijst met foutmeldingen die wordt weergegeven door de weergave [Failure05].

7.6.3. De gegevens van het bereik [Session]

In paragraaf 7.3.6 hebben we gezien dat de sessiegegevens waren ingekapseld in het volgende model [SessionModel]:


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

Dit sessiemodel wordt uitgebreid om de waarden van A en B op te nemen:


using System;
namespace Exemple_03.Models
{
  public class SessionModel
  {
    // de randomgetallengenerator
    public Random Randomizer { get; set; }
    // de waarden van A en B
    public string A { get; set; }
    public string B { get; set; }
  }
}

Het is namelijk noodzakelijk om de waarden van A en B in de sessie op te slaan, zoals blijkt uit de volgende reeks:

Verzoek 1

Verzoek 2

In [4] vinden we de invoer terug die in [1] is gedaan. Er zijn echter twee afzonderlijke verzoeken: HTTP. We weten dat de gegevens tussen twee verzoeken HTTP in de sessie worden opgeslagen. Om ervoor te zorgen dat het tweede verzoek de door het eerste verzoek verzonden waarden kan terugvinden, moeten deze in de sessie worden opgeslagen.

7.6.4. De serveractie [Action05Get]

De actie [Action05Get] is de actie die de eerste, unieke pagina weergeeft. De code ervan is als volgt:


    [HttpGet]
    public ViewResult Action05Get()
    {
      ViewModel05 modèle = new ViewModel05();
      modèle.HeureChargement = DateTime.Now.ToString("hh:mm:ss");
      return View(modèle);
}
  • regel 6: de reeds besproken weergave [Action05Get.cshtml] wordt weergegeven met een sjabloon van het type [ViewModel05];

7.6.5. De clientactie [Calculer]

Laten we de interacties van de gebruiker met de weergaven eens bekijken:

De link [1] is een JavaScript-link:


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

De JavaScript-functie [calculer] is te vinden in het bestand [myScripts-05.js]:


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

De code van de JavaScript-functie [calculer] is als volgt:


// algemene gegevens
var content;
var loading;

function calculer() {
  // eerst de verwijzingen naar DOM
  var formulaire = $("#formulaire");
  // vervolgens validatie van het formulier
  if (!formulaire.validate().form()) {
    // ongeldig formulier - voltooid
    return;
  }
  // we voeren handmatig een Ajax-aanroep uit
  $.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) {
      // weergave van het antwoord van de server
      content.html(jqXHR.responseText);
    }
  })
}

function retourSaisies() {
 ...
}

function effacer() {
  ...
}

// bij het laden van het document
$(document).ready(function () {
  // de referenties van de verschillende componenten van de pagina worden opgehaald
  loading = $("#loading");
  content = $("#content");
  // de geanimeerde afbeelding wordt in de cache opgeslagen
  loading.hide();
});
  • Ter herinnering: de JavaScript-code wordt altijd aan de clientzijde, in de browser, uitgevoerd;
  • regel 44: de functie JS wordt uitgevoerd wanneer het eerste laden van de enkele pagina is voltooid;
  • regel 46: verwijzing naar de geanimeerde afbeelding met id [loading];
  • regel 47: verwijzing naar het gebied met id [content]. Dit gebied ontvangt de deelweergaven [Formulaire05, Success05, Failure05];
  • regels 2-3: de variabelen uit de regels 46-47 worden als globaal gedeclareerd, zodat andere functies er toegang toe hebben. Het zoeken naar elementen op een pagina (regels 46-47) kost rekenkracht. Het is niet nodig deze zoekopdracht te herhalen als dat kan worden vermeden;
  • regel 5: de functie [calculer];
  • regel 7: er wordt een verwijzing naar het formulier opgehaald. De deelweergave [Formulaire05] heeft het de id [formulaire] toegekend;
  • regel 9: deze instructie voert de validaties van het formulier aan de clientzijde uit. Dit was wat ontbrak in de op pagina 183 geconstateerde afwijking. Deze methode wordt geleverd door de bibliotheek [jquery.unobstrusive-ajax] die door de enkele pagina wordt gebruikt:

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

De instructie retourneert [false] als het formulier ongeldig wordt verklaard;

  • regel 11: de Ajax-aanroep naar de server wordt niet uitgevoerd als het formulier ongeldig is;
  • regels 14-32: de Ajax-aanroep naar de server wordt uitgevoerd;
  • regel 15: de URL-bestemming is de serveractie [Action05FaireCalcul];
  • regel 16: deze wordt aangevraagd via een [POST];
  • regel 17: de verzonden waarden. Dit zijn de invoergegevens uit het formulier, in dit geval de waarden van A en B;
  • regels 22-24: als de Ajax-aanroep slaagt, werkt de functie [calculer] het gebied met id [content] bij met de door de server verzonden stream HTML.

Deze feed HTML is de feed die wordt verzonden door de actie [Action05FaireCalcul] waarop de Ajax-aanroep is gericht. De code van deze actie aan de serverzijde is als volgt:


    [HttpPost]
    public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
    {
      // sjabloon
      ViewModel05 modèle = new ViewModel05();
      // het tijdstip van berekening
      modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
      // het sjabloon wordt bijgewerkt
      TryUpdateModel(modèle, postedData);
      if (!ModelState.IsValid)
      {
        // er wordt een fout geretourneerd
        modèle.Erreurs = getListOfMessagesFor(ModelState);
        return PartialView("Failure05", modèle);
      }
...
}
  • regel 1: de actie accepteert alleen een [post];
  • regel 2: de actie retourneert een gedeeltelijke weergave;
  • regel 2: de actie ontvangt als parameters de geposte waarden (postedData) en het sessiemodel (session);
  • regel 5: het sjabloon van de gedeeltelijke weergave wordt aangemaakt;
  • regel 7: het wordt bijgewerkt met het tijdstip van berekening;
  • regel 9: er wordt geprobeerd de geposte waarden op het model toe te passen. De validaties van het model worden dan uitgevoerd. Je kunt je afvragen waarom we deze moeite doen, terwijl de client-side validaties POST al tegenhouden als de ingevoerde gegevens ongeldig zijn. In feite weten we niet zeker waar de POST vandaan komt. Misschien is deze gegenereerd door code die niet van ons is. Daarom moeten we altijd controles uitvoeren aan de serverzijde;
  • regel 10: we testen of de validaties zijn geslaagd;
  • regel 13: als het model ongeldig is, wordt het bijgewerkt met een lijst met fouten. We gaan niet in detail in op de interne methode [getListOfMessagesFor], die vergelijkbaar is met de methode [GetErrorMessagesFor] die op pagina 65 wordt beschreven;
  • regel 14: de deelweergave [Failure05] wordt samen met het bijbehorende sjabloon weergegeven. We herhalen de code van deze weergave;

@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>
  • regels 7-12: de lijst met fouten in het sjabloon wordt weergegeven met behulp van een <ul>-tag.

We herinneren ons dat de functie JS [calculer] aan deoorsprong van de [Post] bij de serveractie [Action05FaireCalcul] zal deze feed HTML in het gebied met id [content] plaatsen. Dit ziet er ongeveer zo uit:

Laten we verdergaan met het bestuderen van de code van de actie [Action05FaireCalcul]:


    [HttpPost]
    public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
    {
      // sjabloon
      ViewModel05 modèle = new ViewModel05();
...
      // de waarden van A en B worden in de sessie opgeslagen
      session.A = modèle.A;
      session.B = modèle.B;
      // tot nu toe geen fouten
      List<string> erreurs = new List<string>();
      // om de andere keer wordt er een fout gesimuleerd
      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);
      }
      // berekeningen
      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);
      // weergave
      return PartialView("Success05", modèle);
}
  • regel 7: het model is als geldig verklaard;
  • regels 8-9: de ingevoerde waarden A en B worden in de sessie opgeslagen. We willen deze kunnen terugvinden in de volgende query;
  • regels 11-22: er wordt één op de twee keer willekeurig een fout gegenereerd;
  • regels 24-29: de vier rekenkundige bewerkingen worden uitgevoerd op de ingevoerde reële getallen;
  • regel 31: de deelweergave [Success05] wordt samen met het bijbehorende model geretourneerd. Deze deelweergave ziet er als volgt uit:

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

We herinneren ons dat de functie JS [calculer], dieoorsprong van [Post] bij de serveractie [Action05FaireCalcul] deze stream HTML in de regio met id [content] plaatst. Dit levert ongeveer het volgende op:

7.6.6. De clientactie [Effacer]

Met de JavaScript-link [Effacer] kan het formulier in de oorspronkelijke staat worden teruggezet:

In het formulier is de link JS [Effacer] als volgt gedefinieerd:


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

De functie JS [effacer] is in het bestand [myScripts-05.js] als volgt gedefinieerd:


// algemene gegevens
var content;
var loading;

function calculer() {
...
}

function retourSaisies() {
...
}

function effacer() {
  // eerst de referenties op de DOM
  var formulaire = $("#formulaire");
  var A = $("#A");
  var B = $("#B");
  // we wijzen geldige waarden toe aan de invoer
  A.val("0");
  B.val("0");
  // vervolgens wordt het formulier gevalideerd om
  // eventuele foutmeldingen te verwijderen
  formulaire.validate().form();
  // en vervolgens worden lege tekenreeksen toegewezen aan de invoervelden
  A.val("");
  B.val("");
}

// bij het laden van het document
$(document).ready(function () {
  // halen we de referenties van de verschillende componenten van de pagina op
  loading = $("#loading");
  content = $("#content");
  // de geanimeerde afbeelding wordt verborgen
  loading.hide();
});
  • regels 15-17: er worden verwijzingen opgehaald naar diverse elementen van het DOM (Document Object Model);
  • regels 19-20: er worden geldige waarden ingevuld in de invoervelden voor de getallen A en B;
  • regel 23: we voeren de validaties aan de clientzijde uit. Aangezien de waarden van A en B geldig zijn, zullen eventuele foutmeldingen die zouden kunnen worden weergegeven, hierdoor verdwijnen;
  • regels 25-26: er worden lege tekenreeksen ingevoerd in de invoervelden voor de getallen A en B;

7.6.7. De clientactie [Retour aux Saisies]

Met de JavaScript-link [Retour aux Saisies] kun je terugkeren naar het formulier nadat je resultaten hebt verkregen:

In het formulier is de link JS [Retour aux Saisies] als volgt gedefinieerd:


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

De functie JS [retourSaisies] is in het bestand [myScripts-05.js] als volgt gedefinieerd:


// globale gegevens
var content;
var loading;

function calculer() {
...
}

function retourSaisies() {
  // er wordt handmatig een Ajax-verzoek gedaan
  $.ajax({
    url: '/Premier/Action05RetourSaisies',
    type: 'POST',
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
      // IMPORTANT !! validatie
      $.validator.unobtrusive.parse($("#formulaire"));
    },
    error: function (jqXHR) {
      content.html(jqXHR.responseText);
    }
  })
}

function effacer() {
...
}

// bij het laden van het document
$(document).ready(function () {
  // de referenties van de verschillende componenten van de pagina worden opgehaald
  loading = $("#loading");
  content = $("#content");
  // de geanimeerde afbeelding wordt in de cache opgeslagen
  loading.hide();
});
  • regels 11-29: een Ajax-aanroep;
  • regel 12: de doel-URL;
  • regel 13: dit wordt aangevraagd door een HTTP-commando POST. Dit is een POST zonder verzonden parameters. Daarom is er geen regel van het type:
    data: formulaire.serialize(),

in de Ajax-aanroep;

  • regel 14: de verwachte stream van de server is een HTML-stream;
  • regels 18-20: deze stream HTML wordt gebruikt om de regio met id [content] bij te werken;

De serveractie [Action05RetourSaisies] is als volgt:


    [HttpPost]
    public PartialViewResult Action05RetourSaisies(SessionModel session)
    {
      // weergave
      return PartialView("Formulaire05", new ViewModel05() { A = session.A, B = session.B });
}
  • regel 2: de actie ontvangt als parameter het sessiesjabloon waarin we eerder de ingevoerde waarden van A en B hebben opgeslagen;
  • regel 5: we retourneren de deelweergave [Formulaire05] met een model van het type [ViewModel05], waarbij we ervoor zorgen dat de velden A en B worden geïnitialiseerd met de waarden van A en B uit de sessie;

Laten we nu teruggaan naar de code van de JavaScript-functie [retourSaisies]:


function retourSaisies() {
  // er wordt handmatig een Ajax-verzoek gedaan
  $.ajax({
    url: '/Premier/Action05RetourSaisies',
    type: 'POST',
    dataType: 'html',
    beforeSend: function () {
      loading.show();
    },
    success: function (data) {
      content.html(data);
    },
    complete: function () {
      loading.hide();
      // IMPORTANT !! validatie
      $.validator.unobtrusive.parse($("#formulaire"));
    },
    error: function (jqXHR) {
      content.html(jqXHR.responseText);
    }
  })
}
  • regel 13: de methode die wordt uitgevoerd wanneer de Ajax-aanroep is voltooid;
  • regel 14: de geanimeerde wachtafbeelding wordt verborgen;
  • regel 16: een voor mij wat onduidelijke instructie die ik op internet heb gevonden om het volgende probleem op te lossen: in het formulier dat via de link [Retour aux saisies] wordt weergegeven, werkten de client-side validaties niet meer. Bij het zoeken naar informatie over de bibliotheek JS [jquery.unobtrusive-ajax] vond ik de oplossing in regel 16. Deze parseert het formulier, wellicht om de client-side validatoren te activeren.

7.7. Een ASP.NET-toepassing op internet beschikbaar maken

Zie paragraaf 9.26.

7.8. Een native Android-applicatie genereren op basis van een applicatie met één pagina APU

Zie paragraaf 9.27.