다음을 통해 공유


AJAX를 사용하여 매핑 시나리오 구현

작성자: Microsoft

PDF 다운로드

ASP.NET MVC 1을 사용하여 작지만 완전한 웹 애플리케이션을 빌드하는 방법을 안내하는 무료 "NerdDinner" 애플리케이션 자습서 의 11단계입니다.

11단계에서는 NerdDinner 애플리케이션에 AJAX 매핑 지원을 통합하여 저녁 식사를 만들거나 편집하거나 보는 사용자가 저녁 식사 위치를 그래픽으로 볼 수 있도록 하는 방법을 보여 줍니다.

ASP.NET MVC 3을 사용하는 경우 MVC 3 또는 MVC Music Store 시작 따라가는 것이 좋습니다.

NerdDinner 11단계: AJAX 맵 통합

이제 AJAX 매핑 지원을 통합하여 애플리케이션을 시각적으로 좀 더 흥미진진하게 만들겠습니다. 이렇게 하면 저녁 식사를 만들거나 편집하거나 보는 사용자가 저녁 식사 위치를 그래픽으로 볼 수 있습니다.

지도 부분 보기 만들기

애플리케이션 내의 여러 위치에서 매핑 기능을 사용할 예정입니다. 코드를 DRY로 유지하기 위해 여러 컨트롤러 작업 및 뷰에서 다시 사용할 수 있는 단일 부분 템플릿 내에 공통 맵 기능을 캡슐화합니다. 이 부분 보기의 이름을 "map.ascx"로 지정하고 \Views\Dinners 디렉터리 내에 만듭니다.

\Views\Dinners 디렉터리를 마우스 오른쪽 단추로 클릭하고 추가> 보기 메뉴 명령을 선택하여 map.ascx 일부를 만들 수 있습니다. 뷰의 이름을 "Map.ascx"로 지정하고, 부분 보기로 검사 강력한 형식의 "Dinner" 모델 클래스를 전달할 것임을 나타냅니다.

보기 추가 대화 상자의 스크린샷 Nerd Dinner dot Models dot Dinner는 데이터 클래스 보기 상자에 기록됩니다.

"추가" 단추를 클릭하면 부분 템플릿이 만들어집니다. 그런 다음 Map.ascx 파일을 업데이트하여 다음 콘텐츠를 갖습니다.

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

첫 번째 <스크립트> 참조는 Microsoft Virtual Earth 6.2 매핑 라이브러리를 가리킵니다. 두 번째 <스크립트> 참조는 곧 만들 map.js 파일을 가리키며 이는 일반적인 Javascript 매핑 논리를 캡슐화합니다. <div id="theMap"> 요소는 Virtual Earth가 맵을 호스트하는 데 사용할 HTML 컨테이너입니다.

그런 다음 이 보기와 관련된 두 개의 JavaScript 함수를 포함하는 포함된 <스크립트> 블록이 있습니다. 첫 번째 함수는 jQuery를 사용하여 페이지가 클라이언트 쪽 스크립트를 실행할 준비가 되면 실행되는 함수를 연결합니다. 가상 지구 맵 컨트롤을 로드하기 위해 Map.js 스크립트 파일 내에서 정의할 LoadMap() 도우미 함수를 호출합니다. 두 번째 함수는 위치를 식별하는 맵에 핀을 추가하는 콜백 이벤트 처리기입니다.

클라이언트 쪽 스크립트 블록 내에서 서버 쪽 <%= %> 블록을 사용하여 JavaScript에 매핑하려는 Dinner의 위도 및 경도를 포함하는 방법을 알아봅니다. 이는 클라이언트 쪽 스크립트에서 사용할 수 있는 동적 값을 출력하는 데 유용한 기술입니다(값을 검색하기 위해 서버에 대한 별도의 AJAX 호출을 요구하지 않아도 되므로 속도가 빨라집니다). %= %> 블록은 <뷰가 서버에서 렌더링될 때 실행되므로 HTML의 출력은 포함된 JavaScript 값으로 끝납니다(예: var latitude = 47.64312;).

Map.js 유틸리티 라이브러리 만들기

이제 맵에 대한 JavaScript 기능을 캡슐화하고 위의 LoadMap 및 LoadPin 메서드를 구현하는 데 사용할 수 있는 Map.js 파일을 만들어 보겠습니다. 프로젝트 내에서 \Scripts 디렉터리를 마우스 오른쪽 단추로 클릭한 다음 "새 항목 추가>" 메뉴 명령을 선택하고 JScript 항목을 선택하고 이름을 "Map.js"로 지정하여 이 작업을 수행할 수 있습니다.

다음은 Virtual Earth와 상호 작용하여 지도를 표시하고 저녁 식사를 위해 위치 핀을 추가하는 Map.js 파일에 추가할 JavaScript 코드입니다.

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

양식 만들기 및 편집과 맵 통합

이제 맵 지원을 기존 만들기 및 편집 시나리오와 통합합니다. 좋은 소식은 매우 쉽게 수행할 수 있으며 컨트롤러 코드를 변경할 필요가 없다는 것입니다. 만들기 및 편집 보기는 일반적인 "DinnerForm" 부분 보기를 공유하여 저녁 식사 양식 UI를 구현하기 때문에 맵을 한 곳에 추가하고 만들기 및 편집 시나리오 모두 사용할 수 있습니다.

\Views\Dinners\DinnerForm.ascx 부분 보기를 열고 새 맵 부분 보기를 포함하도록 업데이트하기만 하면 됩니다. 다음은 맵이 추가되면 업데이트된 DinnerForm의 모양입니다(참고: 간결성을 위해 아래 코드 조각에서 HTML 양식 요소를 생략함).

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

<% } %>

위의 DinnerForm 부분에서는 "DinnerFormViewModel" 형식의 개체를 모델 형식으로 사용합니다(국가의 드롭다운 목록을 채우려면 Dinner 개체와 SelectList가 모두 필요하기 때문). Map partial에는 모델 형식으로 "Dinner" 형식의 개체만 필요하므로 맵 부분을 렌더링할 때 DinnerFormViewModel의 Dinner 하위 속성만 전달합니다.

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

부분 에 추가한 JavaScript 함수는 jQuery를 사용하여 "Address" HTML 텍스트 상자에 "blur" 이벤트를 연결합니다. 사용자가 텍스트 상자를 클릭하거나 탭할 때 발생하는 "포커스" 이벤트에 대해 들어보셨을 것입니다. 반대로 사용자가 텍스트 상자를 종료할 때 발생하는 "흐리게" 이벤트입니다. 위의 이벤트 처리기는 이 경우 위도 및 경도 텍스트 상자 값을 지운 다음 지도에 새 주소 위치를 그립니다. 그런 다음, map.js 파일 내에서 정의한 콜백 이벤트 처리기는 제공한 주소를 기반으로 가상 지구가 반환하는 값을 사용하여 양식의 경도 및 위도 텍스트 상자를 업데이트합니다.

이제 애플리케이션을 다시 실행하고 "호스트 저녁 식사" 탭을 클릭하면 표준 Dinner 양식 요소와 함께 기본 맵이 표시됩니다.

기본 맵이 표시된 호스트 저녁 식사 페이지의 스크린샷

주소를 입력한 다음 탭을 제거하면 맵이 동적으로 업데이트되어 위치가 표시되고 이벤트 처리기가 위도/경도 텍스트 상자를 위치 값으로 채웁니다.

지도가 표시된 괴상한 저녁 식사 페이지의 스크린샷.

새 저녁 식사를 저장한 다음 편집을 위해 다시 열면 페이지가 로드될 때 지도 위치가 표시됩니다.

Nerd Dinners 사이트의 편집 페이지 스크린샷

주소 필드가 변경될 때마다 지도 및 위도/경도 좌표가 업데이트됩니다.

이제 지도에 저녁 식사 위치가 표시되었으므로 위도 및 경도 양식 필드를 표시되는 텍스트 상자에서 숨겨진 요소로 변경할 수도 있습니다(맵이 주소를 입력할 때마다 자동으로 업데이트되기 때문에). 이 작업을 수행하려면 Html.TextBox() HTML 도우미 사용에서 Html.Hidden() 도우미 메서드 사용으로 전환합니다.

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

이제 양식은 좀 더 사용자에게 친숙하며 원시 위도/경도를 표시하지 않습니다(데이터베이스의 각 Dinner와 함께 저장).

괴상한 저녁 식사 페이지의 지도 스크린샷.

맵을 세부 정보 보기와 통합

이제 맵을 만들기 및 편집 시나리오와 통합했으므로 세부 정보 시나리오와도 통합해 보겠습니다. 세부 정보 보기 내에서 % Html.RenderPartial("map"); %를> 호출<하기만 하면 됩니다.

다음은 전체 세부 정보 보기(맵 통합 포함)의 소스 코드는 다음과 같습니다.

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

이제 사용자가 /Dinners/Details/[id] URL로 이동하면 저녁 식사에 대한 세부 정보, 지도에서 저녁 식사의 위치(마우스를 가리켜 저녁 식사의 제목과 주소를 표시하는 푸시 핀이 완성됨)가 표시되고 RSVP에 대한 AJAX 링크가 표시됩니다.

괴상한 저녁 식사 웹 페이지의 스크린샷. 지도가 표시됩니다.

데이터베이스 및 리포지토리에서 위치 검색 구현

AJAX 구현을 완료하려면 사용자가 가까운 저녁 식사를 그래픽으로 검색할 수 있는 애플리케이션의 홈페이지에 지도를 추가해 보겠습니다.

괴상한 저녁 식사의 홈 페이지의 스크린샷. 지도가 표시됩니다.

먼저 데이터베이스 및 데이터 리포지토리 계층 내에서 지원을 구현하여 Dinners에 대한 위치 기반 반경 검색을 효율적으로 수행합니다. SQL 2008의 새로운 지리 공간적 기능을 사용하여 이를 구현하거나, 또는 Gary Dryden이 여기 http://www.codeproject.com/KB/cs/distancebetweenlocations.aspx문서에서 설명한 SQL 함수 접근 방식을 사용할 수 있습니다.

이 기술을 구현하려면 Visual Studio 내에서 "서버 Explorer"을 열고 NerdDinner 데이터베이스를 선택한 다음 그 아래의 "함수" 하위 노드를 마우스 오른쪽 단추로 클릭하고 새 "스칼라 반환 함수"를 만들도록 선택합니다.

Visual Studio의 서버 Explorer 스크린샷 Nerd Dinner 데이터베이스가 선택되고 함수 하위 노드가 선택됩니다. 스칼라 반환 함수가 강조 표시되어 있습니다.

그런 다음, 다음 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

그런 다음 SQL Server "NearestDinners"라고 하는 새 테이블 반환 함수를 만듭니다.

S QL 서버의 스크린샷 Table-Valued 함수가 강조 표시됩니다.

이 "NearestDinners" 테이블 함수는 DistanceBetween 도우미 함수를 사용하여 제공하는 위도 및 경도에서 100마일 이내에 있는 모든 Dinner를 반환합니다.

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

이 함수를 호출하려면 먼저 \Models 디렉터리 내에서 NerdDinner.dbml 파일을 두 번 클릭하여 LINQ to SQL 디자이너를 엽니다.

Models 디렉터리에 있는 Nerd Dinner dot d b m l 파일의 스크린샷

그런 다음 NearestDinners 및 DistanceBetween 함수를 LINQ to SQL 디자이너로 끌어서 LINQ to SQL NerdDinnerDataContext 클래스에 메서드로 추가합니다.

가장 가까운 저녁 식사 및 함수 간 거리 스크린샷

그런 다음 NearestDinner 함수를 사용하여 지정된 위치에서 100마일 이내의 예정된 Dinners를 반환하는 DinnerRepository 클래스에 "FindByLocation" 쿼리 메서드를 노출할 수 있습니다.

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

JSON 기반 AJAX 검색 작업 메서드 구현

이제 새 FindByLocation() 리포지토리 메서드를 활용하여 맵을 채우는 데 사용할 수 있는 Dinner 데이터 목록을 반환하는 컨트롤러 작업 메서드를 구현합니다. 클라이언트에서 JavaScript를 사용하여 쉽게 조작할 수 있도록 이 작업 메서드가 Dinner 데이터를 JSON(JavaScript 개체 표기법) 형식으로 반환합니다.

이를 구현하기 위해 \Controllers 디렉터리를 마우스 오른쪽 단추로 클릭하고 추가> 컨트롤러 메뉴 명령을 선택하여 새 "SearchController" 클래스를 만듭니다. 그런 다음 아래와 같이 새 SearchController 클래스 내에서 "SearchByLocation" 작업 메서드를 구현합니다.

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

SearchController의 SearchByLocation 작업 메서드는 내부적으로 DinnerRepository의 FindByLocation 메서드를 호출하여 근처 저녁 식사 목록을 가져옵니다. 하지만 Dinner 개체를 클라이언트에 직접 반환하는 대신 JsonDinner 개체를 반환합니다. JsonDinner 클래스는 Dinner 속성의 하위 집합을 노출합니다(예: 보안상의 이유로 저녁 식사를 위해 RSVP가 있는 사람의 이름을 공개하지 않음). 또한 Dinner에 존재하지 않고 특정 저녁 식사와 연결된 RSVP 개체 수를 계산하여 동적으로 계산되는 RSVPCount 속성도 포함됩니다.

그런 다음 컨트롤러 기본 클래스에서 Json() 도우미 메서드를 사용하여 JSON 기반 와이어 형식을 사용하여 저녁 식사 시퀀스를 반환합니다. JSON은 간단한 데이터 구조를 나타내는 표준 텍스트 형식입니다. 다음은 작업 메서드에서 반환될 때 두 JsonDinner 개체의 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}]

jQuery를 사용하여 JSON 기반 AJAX 메서드 호출

이제 SearchController의 SearchByLocation 작업 메서드를 사용하도록 NerdDinner 애플리케이션의 홈페이지를 업데이트할 준비가 되었습니다. 이렇게 하려면 /Views/Home/Index.aspx 보기 템플릿을 열고 텍스트 상자, 검색 단추, 맵 및 <dinnerList라는 div> 요소를 갖도록 업데이트합니다.

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

그런 다음 두 개의 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>

첫 번째 JavaScript 함수는 페이지가 처음 로드되면 맵을 로드합니다. 두 번째 JavaScript 함수는 검색 단추에서 JavaScript 클릭 이벤트 처리기를 연결합니다. 단추를 누르면 Map.js 파일에 추가할 FindDinnersGivenLocation() JavaScript 함수를 호출합니다.

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

이 FindDinnersGivenLocation() 함수는 맵을 호출합니다. 가상 지구 컨트롤에서 찾기()를 선택하여 입력한 위치에 가운데에 배치합니다. 가상 지구 맵 서비스가 반환되면 지도입니다. Find() 메서드는 최종 인수로 전달한 콜백UpdateMapDinners 콜백 메서드를 호출합니다.

callbackUpdateMapDinners() 메서드는 실제 작업이 수행되는 위치입니다. jQuery의 $.post() 도우미 메서드를 사용하여 SearchController의 SearchByLocation() 작업 메서드에 대한 AJAX 호출을 수행하여 새로 가운데에 있는 맵의 위도 및 경도를 전달합니다. $.post() 도우미 메서드가 완료될 때 호출되는 인라인 함수를 정의하고 SearchByLocation() 작업 메서드에서 반환된 JSON 형식의 저녁 식사 결과는 "dinners"라는 변수를 사용하여 전달됩니다. 그런 다음 반환된 각 저녁 식사에 대해 foreach를 수행하고 저녁 식사의 위도 및 경도 및 기타 속성을 사용하여 지도에 새 핀을 추가합니다. 또한 지도 오른쪽의 HTML 저녁 식사 목록에 저녁 식사 항목을 추가합니다. 그런 다음, 사용자가 마우스로 가리키면 저녁 식사에 대한 세부 정보가 표시되도록 압정 및 HTML 목록 모두에 대한 호버 이벤트를 연결합니다.

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

이제 애플리케이션을 실행하고 홈페이지를 방문할 때 지도가 표시됩니다. 도시의 이름을 입력하면 지도에 근처의 예정된 저녁 식사가 표시됩니다.

지도가 표시된 Nerd Dinner 홈페이지의 스크린샷.

저녁 식사 위에 마우스를 가져가면 세부 정보가 표시됩니다.

거품형 또는 HTML 목록의 오른쪽에서 저녁 식사 제목을 클릭하면 저녁 식사로 이동됩니다. 그러면 필요에 따라 RSVP에서 다음을 수행할 수 있습니다.

저녁 식사 탐색을 보여 주는 지도가 있는 Nerd Dinner 세부 정보 페이지의 스크린샷

다음 단계

이제 NerdDinner 애플리케이션의 모든 애플리케이션 기능을 구현했습니다. 이제 자동화된 단위 테스트를 사용하도록 설정하는 방법을 살펴보겠습니다.