Partager via


Utiliser AJAX pour implémenter des scénarios de mappage

par Microsoft

Télécharger le PDF

Il s’agit de l’étape 11 d’un didacticiel gratuit sur l’application « NerdDinner » qui explique comment créer une application web petite mais complète à l’aide de ASP.NET MVC 1.

L’étape 11 montre comment intégrer la prise en charge du mappage AJAX dans notre application NerdDinner, ce qui permet aux utilisateurs qui créent, modifient ou consultent des dîners de voir l’emplacement du dîner sous forme graphique.

Si vous utilisez ASP.NET MVC 3, nous vous recommandons de suivre les didacticiels Prise en main Avec MVC 3 ou MVC Music Store.

Étape 11 de NerdDinner : Intégration d’une carte AJAX

Nous allons maintenant rendre notre application un peu plus attrayante visuellement en intégrant la prise en charge du mappage AJAX. Cela permettra aux utilisateurs qui créent, modifient ou consultent des dîners de voir graphiquement l’emplacement du dîner.

Création d’une vue partielle de carte

Nous allons utiliser la fonctionnalité de mappage à plusieurs endroits au sein de notre application. Pour conserver notre code DRY, nous allons encapsuler la fonctionnalité de carte commune dans un seul modèle partiel que nous pouvons réutiliser sur plusieurs actions et vues de contrôleur. Nous allons nommer cette vue partielle « map.ascx » et la créer dans le répertoire \Views\Dinners.

Nous pouvons créer la partie map.ascx en cliquant avec le bouton droit sur le répertoire \Views\Dinners et en choisissant la commande de menu Add-View>. Nous allons nommer la vue « Map.ascx », case activée comme une vue partielle et indiquer que nous allons lui passer une classe de modèle « Dinner » fortement typée :

Capture d’écran de la boîte de dialogue Ajouter un affichage. Nerd Dinner dot Models point Dinner est écrit dans la zone Afficher la classe de données.

Lorsque nous cliquons sur le bouton « Ajouter », notre modèle partiel est créé. Nous allons ensuite mettre à jour le fichier Map.ascx pour avoir le contenu suivant :

<script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2" type="text/javascript"></script>
<script src="/Scripts/Map.js" type="text/javascript"></script>

<div id="theMap">
</div>

<script type="text/javascript">
   
    $(document).ready(function() {
        var latitude = <%=Model.Latitude%>;
        var longitude = <%=Model.Longitude%>;
                
        if ((latitude == 0) || (longitude == 0))
            LoadMap();
        else
            LoadMap(latitude, longitude, mapLoaded);
    });
      
   function mapLoaded() {
        var title = "<%=Html.Encode(Model.Title) %>";
        var address = "<%=Html.Encode(Model.Address) %>";
    
        LoadPin(center, title, address);
        map.SetZoomLevel(14);
    } 
      
</script>

La première <référence de script> pointe vers la bibliothèque de mappage Microsoft Virtual Earth 6.2. La deuxième <référence de script> pointe vers un fichier map.js que nous allons créer prochainement et qui encapsulera notre logique de mappage Javascript commune. L’élément <div id="theMap »> est le conteneur HTML que Virtual Earth utilisera pour héberger la carte.

Nous avons ensuite un bloc de script> incorporé <qui contient deux fonctions JavaScript spécifiques à cette vue. La première fonction utilise jQuery pour connecter une fonction qui s’exécute lorsque la page est prête à exécuter un script côté client. Il appelle une fonction d’assistance LoadMap() que nous allons définir dans notre fichier de script Map.js pour charger le contrôle de carte de terre virtuelle. La deuxième fonction est un gestionnaire d’événements de rappel qui ajoute une broche à la carte qui identifie un emplacement.

Notez que nous utilisons un bloc %= %> côté <serveur dans le bloc de script côté client pour incorporer la latitude et la longitude du Dîner que nous voulons mapper dans javaScript. Il s’agit d’une technique utile pour générer des valeurs dynamiques qui peuvent être utilisées par un script côté client (sans nécessiter un rappel AJAX distinct au serveur pour récupérer les valeurs, ce qui le rend plus rapide). Les <blocs %= %> s’exécutent lorsque l’affichage est rendu sur le serveur. Par conséquent, la sortie du code HTML se termine simplement par des valeurs JavaScript incorporées (par exemple , latitude var = 47.64312;).

Création d’une bibliothèque utilitaire de Map.js

Nous allons maintenant créer le fichier Map.js que nous pouvons utiliser pour encapsuler la fonctionnalité JavaScript pour notre carte (et implémenter les méthodes LoadMap et LoadPin ci-dessus). Pour ce faire, cliquez avec le bouton droit sur le répertoire \Scripts de notre projet, puis choisissez la commande de menu « Ajouter un nouvel élément », sélectionnez l’élément JScript et nommez-le> « Map.js ».

Voici le code JavaScript que nous allons ajouter au fichier Map.js qui interagira avec Virtual Earth pour afficher notre carte et y ajouter des épingles d’emplacements pour nos dîners :

var map = null;
var points = [];
var shapes = [];
var center = null;

function LoadMap(latitude, longitude, onMapLoaded) {
    map = new VEMap('theMap');
    options = new VEMapOptions();
    options.EnableBirdseye = false;

    // Makes the control bar less obtrusize.
    map.SetDashboardSize(VEDashboardSize.Small);
    
    if (onMapLoaded != null)
        map.onLoadMap = onMapLoaded;

    if (latitude != null && longitude != null) {
        center = new VELatLong(latitude, longitude);
    }

    map.LoadMap(center, null, null, null, null, null, null, options);
}

function LoadPin(LL, name, description) {
    var shape = new VEShape(VEShapeType.Pushpin, LL);

    //Make a nice Pushpin shape with a title and description
    shape.SetTitle("<span class=\"pinTitle\"> " + escape(name) + "</span>");
    if (description !== undefined) {
        shape.SetDescription("<p class=\"pinDetails\">" + 
        escape(description) + "</p>");
    }
    map.AddShape(shape);
    points.push(LL);
    shapes.push(shape);
}

function FindAddressOnMap(where) {
    var numberOfResults = 20;
    var setBestMapView = true;
    var showResults = true;

    map.Find("", where, null, null, null,
           numberOfResults, showResults, true, true,
           setBestMapView, callbackForLocation);
}

function callbackForLocation(layer, resultsArray, places,
            hasMore, VEErrorMessage) {
            
    clearMap();

    if (places == null) 
        return;

    //Make a pushpin for each place we find
    $.each(places, function(i, item) {
        description = "";
        if (item.Description !== undefined) {
            description = item.Description;
        }
        var LL = new VELatLong(item.LatLong.Latitude,
                        item.LatLong.Longitude);
                        
        LoadPin(LL, item.Name, description);
    });

    //Make sure all pushpins are visible
    if (points.length > 1) {
        map.SetMapView(points);
    }

    //If we've found exactly one place, that's our address.
    if (points.length === 1) {
        $("#Latitude").val(points[0].Latitude);
        $("#Longitude").val(points[0].Longitude);
    }
}

function clearMap() {
    map.Clear();
    points = [];
    shapes = [];
}

Intégration de la carte avec créer et modifier des formulaires

Nous allons maintenant intégrer la prise en charge de la carte à nos scénarios de création et de modification existants. La bonne nouvelle est que cela est assez facile à faire et ne nous oblige pas à modifier le code de notre contrôleur. Étant donné que nos vues Créer et Modifier partagent une vue partielle commune « DinnerForm » pour implémenter l’interface utilisateur du formulaire de dîner, nous pouvons ajouter la carte à un emplacement et faire en sorte que nos scénarios Créer et Modifier l’utilisent.

Tout ce que nous devons faire est d’ouvrir la vue partielle \Views\Dinners\DinnerForm.ascx et de la mettre à jour pour inclure notre nouvelle carte partielle. Voici à quoi ressemblera dinnerForm mis à jour une fois la carte ajoutée (remarque : les éléments de formulaire HTML sont omis de l’extrait de code ci-dessous pour plus de concision) :

<%= Html.ValidationSummary() %>
 
<% using (Html.BeginForm()) { %>
 
    <fieldset>

        <div id="dinnerDiv">
            <p>
               [HTML Form Elements Removed for Brevity]
            </p>                 
            <p>
               <input type="submit" value="Save"/>
            </p>
        </div>
        
        <div id="mapDiv">    
            <%Html.RenderPartial("Map", Model.Dinner); %>
        </div> 
            
    </fieldset>

    <script type="text/javascript">

        $(document).ready(function() {
            $("#Address").blur(function(evt) {
                $("#Latitude").val("");
                $("#Longitude").val("");

                var address = jQuery.trim($("#Address").val());
                if (address.length < 1)
                    return;

                FindAddressOnMap(address);
            });
        });
    
    </script>

<% } %>

La partie DinnerForm ci-dessus prend un objet de type « DinnerFormViewModel » comme type de modèle (car elle a besoin à la fois d’un objet Dinner, ainsi que d’une SelectList pour remplir la liste déroulante des pays). Notre carte partielle a simplement besoin d’un objet de type « Dinner » comme type de modèle. Par conséquent, lorsque nous restituons la carte partielle, nous lui transmettons uniquement la sous-propriété DinnerFormViewModel :

<% Html.RenderPartial("Map", Model.Dinner); %>

La fonction JavaScript que nous avons ajoutée à la partie utilise jQuery pour attacher un événement « flou » à la zone de texte HTML « Adresse ». Vous avez probablement entendu parler d’événements « focus » qui se déclenchent lorsqu’un utilisateur clique ou onglet dans une zone de texte. L’inverse est un événement « flou » qui se déclenche lorsqu’un utilisateur quitte une zone de texte. Le gestionnaire d’événements ci-dessus efface les valeurs de zone de texte de latitude et de longitude lorsque cela se produit, puis trace le nouvel emplacement d’adresse sur notre carte. Un gestionnaire d’événements de rappel que nous avons défini dans le fichier map.js met ensuite à jour les zones de texte longitude et latitude de notre formulaire à l’aide de valeurs retournées par virtual earth en fonction de l’adresse que nous lui avons donnée.

Maintenant, lorsque nous réexécutons notre application et cliquez sur l’onglet « Dîner hôte », une carte par défaut s’affiche avec nos éléments de formulaire Dîner standard :

Capture d’écran de la page Dîner hôte avec une carte par défaut affichée.

Lorsque nous tapons une adresse, puis que nous en écartons la tabulation, la carte se met à jour dynamiquement pour afficher l’emplacement, et notre gestionnaire d’événements remplit les zones de texte latitude/longitude avec les valeurs d’emplacement :

Capture d’écran de la page Nerd Dinners avec une carte affichée.

Si nous enregistrons le nouveau dîner et que nous l’ouvrons à nouveau pour modification, nous constaterons que l’emplacement de la carte s’affiche lorsque la page se charge :

Capture d’écran de la page Modifier sur le site Nerd Dinners.

Chaque fois que le champ d’adresse est modifié, la carte et les coordonnées de latitude/longitude sont mises à jour.

Maintenant que la carte affiche l’emplacement du dîner, nous pouvons également faire passer les champs de formulaire Latitude et Longitude de zones de texte visibles à des éléments masqués (car la carte les met automatiquement à jour chaque fois qu’une adresse est entrée). Pour ce faire, nous allons passer de l’aide HTML Html.TextBox() à l’utilisation de la méthode d’assistance Html.Hidden() :

<p>
    <%= Html.Hidden("Latitude", Model.Dinner.Latitude)%>
    <%= Html.Hidden("Longitude", Model.Dinner.Longitude)%>
</p>

Et maintenant, nos formulaires sont un peu plus conviviaux et évitent d’afficher la latitude/longitude brute (tout en les stockant avec chaque dîner dans la base de données) :

Capture d’écran d’une carte sur la page Nerd Dinners.

Intégration de la carte à la vue Détails

Maintenant que nous avons intégré la carte à nos scénarios De création et de modification, nous allons également l’intégrer à notre scénario Détails. Il suffit d’appeler <% Html.RenderPartial(« map »); %> dans la vue Détails.

Voici à quoi ressemble le code source de la vue Détails complète (avec intégration de carte) :

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent"runat="server">
    <%= Html.Encode(Model.Title) %>
</asp:Content>

<asp:Content ID="details" ContentPlaceHolderID="MainContent" runat="server">

    <div id="dinnerDiv">

        <h2><%=Html.Encode(Model.Title) %></h2>
        <p>
            <strong>When:</strong> 
            <%=Model.EventDate.ToShortDateString() %> 

            <strong>@</strong>
            <%=Model.EventDate.ToShortTimeString() %>
        </p>
        <p>
            <strong>Where:</strong> 
            <%=Html.Encode(Model.Address) %>,
            <%=Html.Encode(Model.Country) %>
        </p>
         <p>
            <strong>Description:</strong> 
            <%=Html.Encode(Model.Description) %>
        </p>       
        <p>
            <strong>Organizer:</strong> 
            <%=Html.Encode(Model.HostedBy) %>
            (<%=Html.Encode(Model.ContactPhone) %>)
        </p>
    
        <%Html.RenderPartial("RSVPStatus"); %>
        <%Html.RenderPartial("EditAndDeleteLinks"); %>
 
    </div>
    
    <div id="mapDiv">
        <%Html.RenderPartial("map"); %>    
    </div>   
         
</asp:Content>

Et maintenant, lorsqu’un utilisateur accède à une URL /Dinners/Details/[id] il voit les détails du dîner, l’emplacement du dîner sur la carte (avec une broche push qui, en cas de pointage, affiche le titre du dîner et l’adresse de celui-ci), et dispose d’un lien AJAX vers RSVP pour celui-ci :

Capture d’écran de la page web Nerd Dinners. Une carte s’affiche.

Implémentation de la recherche d’emplacement dans notre base de données et notre référentiel

Pour terminer notre implémentation AJAX, ajoutons une carte à la page d’accueil de l’application qui permet aux utilisateurs de rechercher graphiquement des dîners à proximité.

Capture d’écran de la page d’accueil de Nerd Dinners. Une carte s’affiche.

Nous allons commencer par implémenter la prise en charge au sein de notre couche de base de données et de référentiel de données pour effectuer efficacement une recherche de rayon basée sur la localisation pour Dîners. Nous pourrions utiliser les nouvelles fonctionnalités géospatiales de SQL 2008 pour implémenter cela, ou nous pouvons également utiliser une approche de fonction SQL dont Gary Dryden a parlé dans l’article ici : http://www.codeproject.com/KB/cs/distancebetweenlocations.aspx.

Pour implémenter cette technique, nous allons ouvrir « Server Explorer » dans Visual Studio, sélectionner la base de données NerdDinner, puis cliquer avec le bouton droit sur le sous-nœud « functions » en dessous et choisir de créer une nouvelle « fonction scalaire » :

Capture d’écran de l’Explorer de serveur dans Visual Studio. La base de données Nerd Dinner est sélectionnée et le sous-nœud fonctions est sélectionné. La fonction scalaire est mise en surbrillance.

Nous allons ensuite coller la fonction DistanceBetween suivante :

CREATE FUNCTION [dbo].[DistanceBetween](@Lat1 as real,
                @Long1 as real, @Lat2 as real, @Long2 as real)
RETURNS real
AS
BEGIN

DECLARE @dLat1InRad as float(53);
SET @dLat1InRad = @Lat1 * (PI()/180.0);
DECLARE @dLong1InRad as float(53);
SET @dLong1InRad = @Long1 * (PI()/180.0);
DECLARE @dLat2InRad as float(53);
SET @dLat2InRad = @Lat2 * (PI()/180.0);
DECLARE @dLong2InRad as float(53);
SET @dLong2InRad = @Long2 * (PI()/180.0);

DECLARE @dLongitude as float(53);
SET @dLongitude = @dLong2InRad - @dLong1InRad;
DECLARE @dLatitude as float(53);
SET @dLatitude = @dLat2InRad - @dLat1InRad;
/* Intermediate result a. */
DECLARE @a as float(53);
SET @a = SQUARE (SIN (@dLatitude / 2.0)) + COS (@dLat1InRad)
                 * COS (@dLat2InRad)
                 * SQUARE(SIN (@dLongitude / 2.0));
/* Intermediate result c (great circle distance in Radians). */
DECLARE @c as real;
SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a));
DECLARE @kEarthRadius as real;
/* SET kEarthRadius = 3956.0 miles */
SET @kEarthRadius = 6376.5;        /* kms */

DECLARE @dDistance as real;
SET @dDistance = @kEarthRadius * @c;
return (@dDistance);
END

Nous allons ensuite créer une fonction table dans SQL Server que nous appellerons « NearestDinners » :

Capture d’écran du serveur S Q L. Table-Valued fonction est mise en surbrillance.

Cette fonction de table « NearestDinners » utilise la fonction d’assistance DistanceBetween pour renvoyer tous les dîners à moins de 100 milles de la latitude et de la longitude que nous lui fournissons :

CREATE FUNCTION [dbo].[NearestDinners]
      (
      @lat real,
      @long real
      )
RETURNS  TABLE
AS
      RETURN
      SELECT Dinners.DinnerID
      FROM   Dinners 
      WHERE  dbo.DistanceBetween(@lat, @long, Latitude, Longitude) <100

Pour appeler cette fonction, nous allons d’abord ouvrir le concepteur LINQ to SQL en double-cliquant sur le fichier NerdDinner.dbml dans notre répertoire \Models :

Capture d’écran du fichier Nerd Dinner dot d b m l dans le répertoire Models.

Nous allons ensuite faire glisser les fonctions NearestDinners et DistanceBetween vers le concepteur LINQ to SQL, ce qui entraîne leur ajout en tant que méthodes sur notre classe LINQ to SQL NerdDinnerDataContext :

Capture d’écran des fonctions Dîners les plus proches et Distance entre les fonctions.

Nous pouvons ensuite exposer une méthode de requête « FindByLocation » sur notre classe DinnerRepository qui utilise la fonction NearestDinner pour retourner les dîners à venir qui se trouvent à moins de 100 milles de l’emplacement spécifié :

public IQueryable<Dinner> FindByLocation(float latitude, float longitude) {

   var dinners = from dinner in FindUpcomingDinners()
                 join i in db.NearestDinners(latitude, longitude)
                 on dinner.DinnerID equals i.DinnerID
                 select dinner;

   return dinners;
}

Implémentation d’une méthode d’action de recherche AJAX basée sur JSON

Nous allons maintenant implémenter une méthode d’action de contrôleur qui tire parti de la nouvelle méthode de dépôt FindByLocation() pour renvoyer une liste de données Dinner qui peuvent être utilisées pour remplir une carte. Cette méthode d’action retourne les données Dinner dans un format JSON (JavaScript Object Notation) afin qu’elles puissent être facilement manipulées à l’aide de JavaScript sur le client.

Pour l’implémenter, nous allons créer une classe « SearchController » en cliquant avec le bouton droit sur le répertoire \Controllers et en choisissant la commande de menu Ajouter-Contrôleur>. Nous allons ensuite implémenter une méthode d’action « SearchByLocation » dans la nouvelle classe SearchController comme ci-dessous :

public class JsonDinner {
    public int      DinnerID    { get; set; }
    public string   Title       { get; set; }
    public double   Latitude    { get; set; }
    public double   Longitude   { get; set; }
    public string   Description { get; set; }
    public int      RSVPCount   { get; set; }
}

public class SearchController : Controller {

    DinnerRepository dinnerRepository = new DinnerRepository();

    //
    // AJAX: /Search/SearchByLocation

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SearchByLocation(float longitude, float latitude) {

        var dinners = dinnerRepository.FindByLocation(latitude,longitude);

        var jsonDinners = from dinner in dinners
                          select new JsonDinner {
                              DinnerID = dinner.DinnerID,
                              Latitude = dinner.Latitude,
                              Longitude = dinner.Longitude,
                              Title = dinner.Title,
                              Description = dinner.Description,
                              RSVPCount = dinner.RSVPs.Count
                          };

        return Json(jsonDinners.ToList());
    }
}

La méthode d’action SearchByLocation du SearchController appelle en interne la méthode FindByLocation sur DinnerRepository pour obtenir la liste des dîners à proximité. Au lieu de renvoyer les objets Dinner directement au client, il renvoie plutôt des objets JsonDinner. La classe JsonDinner expose un sous-ensemble de propriétés Dinner (par exemple, pour des raisons de sécurité, elle ne divulgue pas les noms des personnes qui ont RSVP pour un dîner). Il inclut également une propriété RSVPCount qui n’existe pas sur Dinner et qui est calculée dynamiquement en comptant le nombre d’objets RSVP associés à un dîner particulier.

Nous utilisons ensuite la méthode d’assistance Json() sur la classe de base controller pour renvoyer la séquence de dîners à l’aide d’un format de connexion basé sur JSON. JSON est un format de texte standard pour représenter des structures de données simples. Voici un exemple de ce à quoi ressemble une liste au format JSON de deux objets JsonDinner lorsqu’elles sont retournées à partir de notre méthode d’action :

[{"DinnerID":53,"Title":"Dinner with the Family","Latitude":47.64312,"Longitude":-122.130609,"Description":"Fun dinner","RSVPCount":2}, 
{"DinnerID":54,"Title":"Another Dinner","Latitude":47.632546,"Longitude":-122.21201,"Description":"Dinner with Friends","RSVPCount":3}]

Appel de la méthode AJAX basée sur JSON à l’aide de jQuery

Nous sommes maintenant prêts à mettre à jour la page d’accueil de l’application NerdDinner pour utiliser la méthode d’action SearchByLocation de SearchController. Pour ce faire, nous allons ouvrir le modèle de vue /Views/Home/Index.aspx et le mettre à jour pour qu’il dispose d’une zone de texte, d’un bouton de recherche, de notre carte et d’un <élément div> nommé dinnerList :

<h2>Find a Dinner</h2>

<div id="mapDivLeft">

    <div id="searchBox">
        Enter your location: <%=Html.TextBox("Location") %>
        <input id="search" type="submit" value="Search"/>
    </div>

    <div id="theMap">
    </div>

</div>

<div id="mapDivRight">
    <div id="dinnerList"></div>
</div>

Nous pouvons ensuite ajouter deux fonctions JavaScript à la page :

<script type="text/javascript">

    $(document).ready(function() {
        LoadMap();
    });

    $("#search").click(function(evt) {
        var where = jQuery.trim($("#Location").val());
        if (where.length < 1) 
            return;

        FindDinnersGivenLocation(where);
    });

</script>

La première fonction JavaScript charge la carte lors du premier chargement de la page. La deuxième fonction JavaScript connecte un gestionnaire d’événements de clic JavaScript sur le bouton de recherche. Lorsque le bouton est enfoncé, il appelle la fonction JavaScript FindDinnersGivenLocation() que nous allons ajouter à notre fichier Map.js :

function FindDinnersGivenLocation(where) {
    map.Find("", where, null, null, null, null, null, false,
       null, null, callbackUpdateMapDinners);
}

Cette fonction FindDinnersGivenLocation() appelle map. Find() sur virtual Earth Control pour le centrer sur l’emplacement entré. Lorsque le service de carte de terre virtuelle retourne, la carte. La méthode Find() appelle la méthode de rappel callbackUpdateMapDinners que nous avons passée comme argument final.

La méthode callbackUpdateMapDinners() est l’emplacement où le travail réel est effectué. Il utilise la méthode d’assistance $.post() de jQuery pour effectuer un appel AJAX à la méthode d’action SearchByLocation() de notre SearchController, en lui transmettant la latitude et la longitude de la carte nouvellement centrée. Il définit une fonction inline qui sera appelée lorsque la méthode d’assistance $.post() se terminera, et les résultats de dîner au format JSON retournés par la méthode d’action SearchByLocation() lui seront transmis à l’aide d’une variable appelée « dîners ». Il effectue ensuite une foreach sur chaque dîner retourné, et utilise la latitude et la longitude du dîner et d’autres propriétés pour ajouter une nouvelle épingle sur la carte. Il ajoute également une entrée de dîner à la liste HTML des dîners à droite de la carte. Il envoie ensuite un événement de pointage pour les épingles et la liste HTML afin que les détails du dîner s’affichent lorsqu’un utilisateur les survole :

function callbackUpdateMapDinners(layer, resultsArray, places, hasMore, VEErrorMessage) {

    $("#dinnerList").empty();
    clearMap();
    var center = map.GetCenter();

    $.post("/Search/SearchByLocation", { latitude: center.Latitude, 
                                         longitude: center.Longitude },     
    function(dinners) {
        $.each(dinners, function(i, dinner) {

            var LL = new VELatLong(dinner.Latitude, 
                                   dinner.Longitude, 0, null);

            var RsvpMessage = "";

            if (dinner.RSVPCount == 1)
                RsvpMessage = "" + dinner.RSVPCount + "RSVP";
            else
                RsvpMessage = "" + dinner.RSVPCount + "RSVPs";

            // Add Pin to Map
            LoadPin(LL, '<a href="/Dinners/Details/' + dinner.DinnerID + '">'
                        + dinner.Title + '</a>',
                        "<p>" + dinner.Description + "</p>" + RsvpMessage);

            //Add a dinner to the <ul> dinnerList on the right
            $('#dinnerList').append($('<li/>')
                            .attr("class", "dinnerItem")
                            .append($('<a/>').attr("href",
                                      "/Dinners/Details/" + dinner.DinnerID)
                            .html(dinner.Title))
                            .append(" ("+RsvpMessage+")"));
        });

        // Adjust zoom to display all the pins we just added.
        map.SetMapView(points);

        // Display the event's pin-bubble on hover.
        $(".dinnerItem").each(function(i, dinner) {
            $(dinner).hover(
                function() { map.ShowInfoBox(shapes[i]); },
                function() { map.HideInfoBox(shapes[i]); }
            );
        });
    }, "json");

Et maintenant, lorsque nous exécutons l’application et que nous visitons la page d’accueil, une carte s’affiche. Lorsque nous entrons le nom d’une ville, la carte affiche les prochains dîners près d’elle:

Capture d’écran de la page d’accueil Nerd Dinner avec une carte affichée.

En pointant sur un dîner, vous verrez les détails à ce sujet.

En cliquant sur le titre Dîner dans la bulle ou sur le côté droit de la liste HTML, nous accédons au dîner, que nous pouvons ensuite éventuellement RSVP pour :

Capture d’écran de la page de détails nerd Dinner avec une carte montrant la navigation vers un dîner.

étape suivante

Nous avons maintenant implémenté toutes les fonctionnalités d’application de notre application NerdDinner. Voyons maintenant comment nous pouvons activer le test unitaire automatisé de celui-ci.