Korzystanie z technologii AJAX w celu implementacji scenariuszy mapowania

autor: Microsoft

Pobierz plik PDF

Jest to krok 11 bezpłatnego samouczka aplikacji "NerdDinner" , który zawiera instrukcje tworzenia małej, ale kompletnej aplikacji internetowej przy użyciu ASP.NET MVC 1.

Krok 11 pokazuje, jak zintegrować obsługę mapowania AJAX z naszą aplikacją NerdDinner, umożliwiając użytkownikom, którzy tworzą, edytują lub wyświetlają kolacje, aby wyświetlić lokalizację kolacji graficznie.

Jeśli używasz ASP.NET MVC 3, zalecamy skorzystanie z samouczków Wprowadzenie With MVC 3 lub MVC Music Store.

NerdDinner — krok 11: integrowanie mapy AJAX

Teraz sprawimy, że nasza aplikacja będzie nieco bardziej ekscytująca wizualnie, integrując obsługę mapowania AJAX. Umożliwi to użytkownikom, którzy tworzą, edytują lub wyświetlają kolacje, aby wyświetlić lokalizację kolacji graficznie.

Tworzenie widoku częściowego mapy

Będziemy używać funkcji mapowania w kilku miejscach w naszej aplikacji. Aby zachować dry kodu, hermetyzujemy wspólną funkcjonalność mapy w ramach jednego szablonu częściowego, którego możemy ponownie używać w wielu akcjach i widokach kontrolera. Nadamy temu widokowi nazwę "map.ascx" i utworzymy go w katalogu \Views\Dinners.

Możemy utworzyć plik map.ascx częściowo, klikając prawym przyciskiem myszy katalog \Views\Dinners i wybierając polecenie menu Dodaj-Widok>. Nadamy widokowi nazwę "Map.ascx", sprawdzimy go jako widok częściowy i wskażemy, że przekażemy mu silnie typizację klasy modelu "Dinner":

Zrzut ekranu przedstawiający okno dialogowe Dodawanie widoku. Nerd Dinner dot Models dot Dinner (Kolacja) jest zapisywana w polu Wyświetl klasę danych.

Po kliknięciu przycisku "Dodaj" zostanie utworzony nasz szablon częściowy. Następnie zaktualizujemy plik Map.ascx, aby miał następującą zawartość:

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

Pierwsze <odwołanie do skryptu> wskazuje bibliotekę mapowania Microsoft Virtual Earth 6.2. Drugi <skrypt> odwołuje się do pliku map.js, który wkrótce utworzymy, który będzie hermetyzować naszą wspólną logikę mapowania języka JavaScript. Element <div id="theMap"> jest kontenerem HTML, który będzie używany przez Wirtualną Ziemię do hostowania mapy.

Następnie mamy osadzony <blok skryptu> , który zawiera dwie funkcje języka JavaScript specyficzne dla tego widoku. Pierwsza funkcja używa metody jQuery do podłączania funkcji, która jest wykonywana, gdy strona jest gotowa do uruchomienia skryptu po stronie klienta. Wywołuje ona funkcję pomocnika LoadMap(), którą zdefiniujemy w pliku skryptu Map.js w celu załadowania kontrolki mapy ziemi wirtualnej. Druga funkcja to procedura obsługi zdarzeń wywołania zwrotnego, która dodaje pinezkę do mapy, która identyfikuje lokalizację.

Zwróć uwagę, że używamy bloku %= %> po stronie serwera w bloku skryptu po stronie <klienta, aby osadzić szerokość geograficzną i długość geograficzną kolacji, którą chcemy zamapować w języku JavaScript. Jest to przydatna technika do wyprowadzania wartości dynamicznych, które mogą być używane przez skrypt po stronie klienta (bez konieczności oddzielnego wywołania AJAX z powrotem do serwera w celu pobrania wartości — co sprawia, że jest szybsze). <Bloki %= %> będą wykonywane, gdy widok jest renderowane na serwerze — dlatego dane wyjściowe kodu HTML będą po prostu zawierać osadzone wartości języka JavaScript (na przykład: var latitude = 47.64312;).

Tworzenie biblioteki narzędzi Map.js

Teraz utwórzmy plik Map.js, którego możemy użyć do hermetyzacji funkcji języka JavaScript dla naszej mapy (i zaimplementuj metody LoadMap i LoadPin powyżej). Możemy to zrobić, klikając prawym przyciskiem myszy katalog \Scripts w naszym projekcie, a następnie wybierając polecenie menu "Dodaj nowy> element", wybierz element JScript i nadaj mu nazwę "Map.js".

Poniżej znajduje się kod JavaScript, który dodamy do pliku Map.js, który będzie współdziałał z Wirtualną Ziemią, aby wyświetlić naszą mapę i dodać do niej pinezki dla naszych kolacji:

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 = [];
}

Integrowanie mapy z formularzami tworzenia i edytowania

Teraz zintegrujemy obsługę mapy z istniejącymi scenariuszami tworzenia i edytowania. Dobrą wiadomością jest to, że jest to dość łatwe do zrobienia i nie wymaga od nas zmiany żadnego kodu kontrolera. Ponieważ nasze widoki tworzenia i edytowania mają wspólny widok częściowy "DinnerForm" w celu zaimplementowania interfejsu użytkownika formularza kolacji, możemy dodać mapę w jednym miejscu i korzystać z niej zarówno w scenariuszach tworzenia, jak i edytowania.

Wszystko, co musimy zrobić, to otworzyć widok częściowy \Views\Dinners\DinnerForm.ascx i zaktualizować go, aby uwzględnić naszą nową mapę częściową. Poniżej przedstawiono wygląd zaktualizowanego formularza DinnerForm po dodaniu mapy (uwaga: elementy formularza HTML zostaną pominięte w poniższym fragmencie kodu w celu zwięzłości):

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

<% } %>

Powyższy element DinnerForm przyjmuje obiekt typu "DinnerFormViewModel" jako typ modelu (ponieważ wymaga zarówno obiektu Dinner, jak i listy selectlist, aby wypełnić listę rozwijaną krajów). Nasza mapa częściowa potrzebuje obiektu typu "Dinner" jako typu modelu, a więc gdy renderujemy mapę częściową, przekazujemy tylko właściwość podrzędną DinnerFormViewModel do niego:

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

Funkcja Języka JavaScript dodawana do częściowej funkcji używa biblioteki jQuery w celu dołączenia zdarzenia "rozmycia" do pola tekstowego HTML "Address". Prawdopodobnie znasz zdarzenia "koncentracji uwagi", które są uruchamiane, gdy użytkownik kliknie lub karty w pole tekstowe. Przeciwieństwem jest zdarzenie "rozmycia", które jest uruchamiane, gdy użytkownik zamyka pole tekstowe. Powyższa procedura obsługi zdarzeń czyści wartości pól tekstowych szerokości i długości geograficznej, gdy tak się stanie, a następnie wykreśli nową lokalizację adresu na naszej mapie. Procedura obsługi zdarzeń wywołania zwrotnego zdefiniowana w pliku map.js zaktualizuje pola tekstowe długości i szerokości geograficznej w formularzu przy użyciu wartości zwróconych przez wirtualną ziemię na podstawie podanego adresu.

Teraz po ponownym uruchomieniu aplikacji i kliknięciu karty "Kolacja hosta" zostanie wyświetlona mapa domyślna wraz z naszymi standardowymi elementami formularza obiadu:

Zrzut ekranu przedstawiający stronę Host Dinner (Kolacja hosta) z wyświetloną domyślną mapą.

Po wpiseniu adresu, a następnie naciśnięciu klawisza Tab, mapa zostanie dynamicznie zaktualizowana, aby wyświetlić lokalizację, a nasz program obsługi zdarzeń wypełni pola tekstowe szerokości/długości geograficznej wartościami lokalizacji:

Zrzut ekranu przedstawiający stronę Kolacje Nerd z wyświetloną mapą.

Jeśli zapiszemy nową kolację, a następnie otworzymy ją ponownie do edycji, zauważymy, że lokalizacja mapy jest wyświetlana po załadowaniu strony:

Zrzut ekranu przedstawiający stronę Edytuj w witrynie Kolacje Nerd.

Za każdym razem, gdy pole adresu zostanie zmienione, mapa i współrzędne szerokości/długości geograficznej zostaną zaktualizowane.

Teraz, gdy mapa wyświetla lokalizację kolacji, możemy również zmienić pola formularza Szerokość geograficzna i Długość geograficzna z widocznych pól tekstowych, aby zamiast tego były elementami ukrytymi (ponieważ mapa jest automatycznie aktualizowana przy każdym wprowadzeniu adresu). Aby to zrobić, przełączymy się z używania pomocnika HTML.TextBox() do korzystania z metody pomocnika Html.Hidden():

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

A teraz nasze formularze są nieco bardziej przyjazne dla użytkownika i unikaj wyświetlania nieprzetworzonych szerokości/długości geograficznych (przy jednoczesnym przechowywaniu ich z każdą kolacją w bazie danych):

Zrzut ekranu przedstawiający mapę na stronie Kolacje Nerd.

Integrowanie mapy z widokiem szczegółów

Teraz, gdy mapa jest zintegrowana z naszymi scenariuszami tworzenia i edytowania, zintegrujmy ją również z naszym scenariuszem Szczegóły. Wystarczy wywołać metodę <% Html.RenderPartial("map"); %> w widoku Szczegóły.

Poniżej znajduje się kod źródłowy pełnego widoku Szczegóły (z integracją mapy):

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

A teraz, gdy użytkownik przejdzie do adresu URL /Dinners/Details/[id], zobaczy szczegóły dotyczące kolacji, lokalizację kolacji na mapie (wraz z pinezką, która po umieszczeniu wskaźnika myszy na ekranie wyświetla tytuł kolacji i adres), a także link AJAX do RSVP dla niego:

Zrzut ekranu przedstawiający stronę internetową Kolacje Nerd. Zostanie wyświetlona mapa.

Implementowanie wyszukiwania lokalizacji w naszej bazie danych i repozytorium

Aby zakończyć implementację AJAX, dodajmy mapę do strony głównej aplikacji, która umożliwia użytkownikom graficzne wyszukiwanie kolacji w pobliżu.

Zrzut ekranu przedstawiający stronę główną kolacji Nerd. Zostanie wyświetlona mapa.

Zaczniemy od zaimplementowania obsługi w warstwie bazy danych i repozytorium danych w celu wydajnego przeprowadzania wyszukiwania promienia opartego na lokalizacji na potrzeby kolacji. Możemy użyć nowych funkcji geoprzestrzennych programu SQL 2008 , aby to zaimplementować, lub alternatywnie możemy użyć metody funkcji SQL, którą Gary Dryden omówił w artykule tutaj: http://www.codeproject.com/KB/cs/distancebetweenlocations.aspx.

Aby zaimplementować tę technikę, otworzymy "Eksplorator serwera" w programie Visual Studio, wybierz bazę danych NerdDinner, a następnie kliknij prawym przyciskiem myszy podwęźle "functions" i wybierz nową funkcję "Scalar-valued":

Zrzut ekranu przedstawiający Eksploratora serwera w programie Visual Studio. Wybrano bazę danych Nerd Dinner i wybrano podwęźle funkcji. Funkcja wartości skalarna jest wyróżniona.

Następnie wklejymy następującą funkcję DistanceBetween:

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

Następnie utworzymy nową funkcję z wartością tabeli w SQL Server, którą wywołamy "NearestDinners":

Zrzut ekranu przedstawiający serwer S Q L. funkcja Table-Valued jest wyróżniona.

Ta funkcja tabeli "NearestDinners" używa funkcji pomocnika DistanceBetween, aby zwrócić wszystkie kolacje w odległości 100 mil od szerokości geograficznej i długości geograficznej, do których ją podajemy:

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

Aby wywołać tę funkcję, najpierw otworzymy projektanta LINQ to SQL, klikając dwukrotnie plik NerdDinner.dbml w katalogu \Models:

Zrzut ekranu przedstawiający plik Nerd Dinner dot d b m l w katalogu Models.

Następnie przeciągniemy funkcje NearestDinners i DistanceBetween do projektanta LINQ to SQL, co spowoduje dodanie ich jako metod w klasie LINQ to SQL NerdDinnerDataContext:

Zrzut ekranu przedstawiający funkcje Najbliższe kolacje i Odległość między funkcjami.

Następnie możemy uwidocznić metodę zapytania "FindByLocation" w klasie DinnerRepository, która używa funkcji NearestDinner, aby zwrócić nadchodzące kolacje znajdujące się w odległości 100 mil od określonej lokalizacji:

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

Implementowanie metody akcji wyszukiwania AJAX opartej na formacie JSON

Teraz zaimplementujemy metodę akcji kontrolera, która korzysta z nowej metody repozytorium FindByLocation(), aby zwrócić listę danych kolacji, których można użyć do wypełnienia mapy. Ta metoda akcji zwróci dane kolacji w formacie JSON (JavaScript Object Notation), aby można było łatwo manipulować przy użyciu języka JavaScript na kliencie.

Aby to zaimplementować, utworzymy nową klasę "SearchController", klikając prawym przyciskiem myszy katalog \Controllers i wybierając polecenie menu Dodaj kontroler>. Następnie zaimplementujemy metodę akcji "SearchByLocation" w nowej klasie SearchController, tak jak poniżej:

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

Metoda akcji SearchByLocation elementu SearchController wewnętrznie wywołuje metodę FindByLocation w repozytorium DinnerRepository, aby uzyskać listę pobliskich kolacji. Zamiast zwracać obiekty Dinner bezpośrednio do klienta, zamiast tego zwraca obiekty JsonDinner. Klasa JsonDinner ujawnia podzbiór właściwości Dinner (na przykład: ze względów bezpieczeństwa nie ujawnia nazwisk osób, które mają RSVP'd na kolację). Zawiera również właściwość RSVPCount, która nie istnieje w kolacji i która jest obliczana dynamicznie przez zliczanie liczby obiektów RSVP skojarzonych z określoną kolacją.

Następnie używamy metody pomocnika Json() w klasie bazowej Kontroler, aby zwrócić sekwencję kolacji przy użyciu formatu przewodu opartego na formacie JSON. JSON to standardowy format tekstowy do reprezentowania prostych struktur danych. Poniżej przedstawiono przykładowy wygląd listy obiektów JsonDinner sformatowanych w formacie JSON:

[{"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}]

Wywoływanie metody AJAX opartej na formacie JSON przy użyciu metody jQuery

Teraz możemy zaktualizować stronę główną aplikacji NerdDinner, aby użyć metody akcji SearchByLocation searchController. W tym celu otworzymy szablon widoku /Views/Home/Index.aspx i zaktualizujemy go, aby mieć pole tekstowe, przycisk wyszukiwania, mapę i <element div> o nazwie 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>

Następnie możemy dodać do strony dwie funkcje języka JavaScript:

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

Pierwsza funkcja języka JavaScript ładuje mapę po pierwszym załadowaniu strony. Druga funkcja JavaScript podłącza procedurę obsługi zdarzeń kliknięcia języka JavaScript na przycisku wyszukiwania. Po naciśnięciu przycisku wywołuje funkcję JavaScript FindDinnersGivenLocation(), którą dodamy do pliku Map.js:

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

Ta funkcja FindDinnersGivenLocation() wywołuje mapę. Znajdź() w kontrolce Virtual Earth, aby wyśrodkować ją w wprowadzonej lokalizacji. Gdy zostanie zwrócona usługa mapy ziemi wirtualnej, mapa. Metoda Find() wywołuje metodę wywołania zwrotnego callbackUpdateMapDinners, która została przekazana jako ostatni argument.

Metoda callbackUpdateMapDinners() to miejsce, w którym wykonywana jest rzeczywista praca. Używa metody pomocniczej $.post() jQuery do wykonania wywołania AJAX do metody akcji SearchByLocation() SearchController — przekazując ją szerokość geograficzną i długość geograficzną nowo wyśrodkowanej mapy. Definiuje funkcję śródliniową, która zostanie wywołana po zakończeniu metody pomocnika $.post(), a wyniki kolacji sformatowane w formacie JSON zwrócone z metody akcji SearchByLocation() zostaną przekazane przy użyciu zmiennej o nazwie "kolacje". Następnie wykonuje foreach nad każdą zwróconą kolacją i używa szerokości geograficznej i długości geograficznej kolacji oraz innych właściwości, aby dodać nowy pin na mapie. Dodaje również wpis kolacji do listy HTML kolacji po prawej stronie mapy. Następnie przesunie zdarzenie aktywowania zarówno dla wypychań, jak i listy HTML, aby szczegóły dotyczące kolacji zostały wyświetlone, gdy użytkownik najecha kursorem na nich:

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");

A teraz, gdy uruchomimy aplikację i odwiedzimy stronę główną, zostanie wyświetlona mapa. Po wprowadzeniu nazwy miasta mapa wyświetli nadchodzące kolacje w pobliżu:

Zrzut ekranu przedstawiający stronę główną Nerd Dinner z wyświetloną mapą.

Zatrzymanie wskaźnika myszy na kolacji spowoduje wyświetlenie szczegółowych informacji o tym.

Kliknięcie tytułu kolacji w bąbelku lub po prawej stronie listy HTML spowoduje przejście do kolacji , którą możemy następnie opcjonalnie RSVP:

Zrzut ekranu przedstawiający stronę szczegółów kolacji Nerd z mapą przedstawiającą nawigację do kolacji.

Następny krok

Teraz zaimplementowaliśmy wszystkie funkcje aplikacji naszej aplikacji NerdDinner. Przyjrzyjmy się teraz, jak możemy włączyć automatyczne testowanie jednostkowe.