p2p.wrox.com Forums

p2p.wrox.com Forums (http://p2p.wrox.com/)
-   BOOK: Professional ASP.NET MVC 2 (http://p2p.wrox.com/book-professional-asp-net-mvc-2-588/)
-   -   Map Logic (http://p2p.wrox.com/book-professional-asp-net-mvc-2/80916-map-logic.html)

aidso September 14th, 2010 06:55 AM

Map Logic
I must have repeated this tutorial 4 times now and I cannot see what is going wrong.

Starting on Page 127 I have created the partial template and the Map.js file. However when I run it, I am getting an exception being thrown in the LoadMap() method of the Map.js file stating that "Microsoft JScript runtime error: 'NerdDinner' is undefined". Anyone know what could be up?

This is getting really frustrating now with the number of errors so early into the book.


flyinhawaiian October 8th, 2010 06:49 PM

Super frustrating for me too. It took me all afternoon to figure it out.

Code snippet 1-91.txt (for Map.js) appears to be incorrect. The following Map.js is what I got from a finished and working NerdDinner and inserted into my NerdDinner. The main difference I see is the NerdDinner prefix. Be aware there seems to be 3 different finished samples, the original one, MVC2 (the one in the book), and the ever changing one on Codeplex.

I discovered later on, it may not be a good idea to just copy-paste the Codeplex version (below) into your project. Some of the functions expect dinner to have more properties than what is shown in the book. In the event this doesnt work, it may be safer to just add the "NerdDinner" prefix to your existing Map.js instead of replacing the whole thing.

There may be a better way to fix this. But for now, this is it.


function NerdDinner() { }

NerdDinner.MapDivId = 'theMap';
NerdDinner._map = null;
NerdDinner._points = [];
NerdDinner._shapes = [];

NerdDinner.LoadMap = function (latitude, longitude, onMapLoaded) {
    NerdDinner._map = new VEMap(NerdDinner.MapDivId);

    var options = new VEMapOptions();

    options.EnableBirdseye = false

    // Makes the control bar less obtrusize.

    if (onMapLoaded != null)
        NerdDinner._map.onLoadMap = onMapLoaded;

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

    NerdDinner._map.LoadMap(center, null, null, null, null, null, null, options);

NerdDinner.ClearMap = function () {
    NerdDinner._points = [];
    NerdDinner._shapes = [];

NerdDinner.LoadPin = function (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>");


NerdDinner.FindAddressOnMap = function (where) {
    var numberOfResults = 1;
    var setBestMapView = true;
    var showResults = true;
    var defaultDisambiguation = true;

    NerdDinner._map.Find("", where, null, null, null,
                        numberOfResults, showResults, true, defaultDisambiguation,
                        setBestMapView, NerdDinner._callbackForLocation);

NerdDinner._callbackForLocation = function (layer, resultsArray, places, hasMore, VEErrorMessage) {

    if (places == null) {

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

        NerdDinner.LoadPin(LL, item.Name, description);

    //Make sure all pushpins are visible
    if (NerdDinner._points.length > 1) {

    //If we've found exactly one place, that's our address.
    //lat/long precision was getting lost here with toLocaleString, changed to toString
    if (NerdDinner._points.length === 1) {

NerdDinner.FindDinnersGivenLocation = function (where) {
    NerdDinner._map.Find("", where, null, null, null, null, null, false,
                        null, null, NerdDinner._callbackUpdateMapDinners);

NerdDinner.FindMostPopularDinners = function (limit) {
    $.post("/Search/GetMostPopularDinners", { "limit": limit }, NerdDinner._renderDinners, "json");

NerdDinner._callbackUpdateMapDinners = function (layer, resultsArray, places, hasMore, VEErrorMessage) {
    var center = NerdDinner._map.GetCenter();

          { latitude: center.Latitude, longitude: center.Longitude },

NerdDinner._renderDinners = function (dinners) {


    $.each(dinners, function (i, dinner) {

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

        // Add Pin to Map
        NerdDinner.LoadPin(LL, _getDinnerLinkHTML(dinner), _getDinnerDescriptionHTML(dinner));

        //Add a dinner to the <ul> dinnerList on the right
                        .attr("class", "dinnerItem")
                        .append(_getDinnerDate(dinner, "mmm d"))
                        .append(" with " + _getRSVPMessage(dinner.RSVPCount)));

    // Adjust zoom to display all the pins we just added.
    if (NerdDinner._points.length > 1) {

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

    function _getDinnerDate(dinner, formatStr) {
        //return '<strong>' + _dateDeserialize(dinner.EventDate).format(formatStr) + '</strong>'; // Codeplex version
        return '<strong>' + _dateDeserialize(dinner.EventDate).toDateString() + '</strong>';

    function _getDinnerLinkHTML(dinner) {
        //return '<a href="' + dinner.Url + '">' + dinner.Title + '</a>';  // Codeplex version has .Url.  The book does not do this.
        return '<a href="' + '/Dinners/Details/' + dinner.DinnerID + '">' + dinner.Title + '</a>';

    function _getDinnerDescriptionHTML(dinner) {
        return '<p>' + _getDinnerDate(dinner, "mmmm d, yyyy") + '</p><p>' + dinner.Description + '</p>' + _getRSVPMessage(dinner.RSVPCount);

    function _dateDeserialize(dateStr) {
        return eval('new' + dateStr.replace(/\//g, ' '));

    function _getRSVPMessage(RSVPCount) {
        var rsvpMessage = "" + RSVPCount + " RSVP";

        if (RSVPCount > 1)
            rsvpMessage += "s";

        return rsvpMessage;

NerdDinner.dragShape = null;
NerdDinner.dragPixel = null;

NerdDinner.EnableMapMouseClickCallback = function () {
    NerdDinner._map.AttachEvent("onmousedown", NerdDinner.onMouseDown);
    NerdDinner._map.AttachEvent("onmouseup", NerdDinner.onMouseUp);
    NerdDinner._map.AttachEvent("onmousemove", NerdDinner.onMouseMove);

NerdDinner.onMouseDown = function (e) {
    if (e.elementID != null) {
        NerdDinner.dragShape = NerdDinner._map.GetShapeByID(e.elementID);
        return true;

NerdDinner.onMouseUp = function (e) {
    if (NerdDinner.dragShape != null) {
        var x = e.mapX;
        var y = e.mapY;
        NerdDinner.dragPixel = new VEPixel(x, y);
        var LatLong = NerdDinner._map.PixelToLatLong(NerdDinner.dragPixel);
        NerdDinner.dragShape = null;

        NerdDinner._map.FindLocations(LatLong, NerdDinner.getLocationResults);

NerdDinner.onMouseMove = function (e) {
    if (NerdDinner.dragShape != null) {
        var x = e.mapX;
        var y = e.mapY;
        NerdDinner.dragPixel = new VEPixel(x, y);
        var LatLong = NerdDinner._map.PixelToLatLong(NerdDinner.dragPixel);
        return true;

NerdDinner.getLocationResults = function (locations) {
    if (locations) {
        var currentAddress = $("#Dinner_Address").val();
        if (locations[0].Name != currentAddress) {
            var answer = confirm("Bing Maps returned the address '" + locations[0].Name + "' for the pin location. Click 'OK' to use this address for the event, or 'Cancel' to use the current address of '" + currentAddress + "'");
            if (answer) {

flyinhawaiian October 8th, 2010 07:09 PM

Also, in DinnerForm.ascx, you'll get a runtime error because of this line:


Change it to:


ajameson October 10th, 2010 01:46 PM

Map position totally wrong
Having run into the same issue, I've done as you suggest and although I now get the map showing up, it looks like the CSS is completely messed up.

I can see a grey box that looks like it's supposed to contain the map. However, this box isn't in the same part of the page as the books suggests it should be. Furthermore - and more importantly - the map isn't in this box. Instead, the map appears at the top-left of the page as if it's been absolutely-positioned. AND, the "Bing" logo is in the bottom-left of the screen (rather than the bottom-left of the map) and the legend is in the bottom-right.

It looks like the map isn't being contained within the "theMap" div.


Any ideas or suggestions? It's quite difficult to proceed through the rest of chapter while the map is blocking most of the controls on the page.

flyinhawaiian October 11th, 2010 12:59 PM

Mine isn't exactly showing up in the right place either. But a little closer. First, does your div tag have an id?

    <div id="mapDiv">
        <% Html.RenderPartial("map"); %>   

2nd, does your site.css have the same id?

    float: right;
    width: 522px;

ajameson October 11th, 2010 02:11 PM

That's not the root of the problem
No, there's no CSS for #mapDiv anywhere in my solution.

In retrospect, this seems a little silly. However, while following the book, at no point have I been told to create a style for the mapDiv.

Also, even if there was a CSS selector for #mapDiv, this wouldn't explain why the map is 'blowing up' all over the page, i.e. the map in the top-left, "bing" in the bottom-left, and the map legend in the bottom-right of the webpage, entirely outside the div that's supposed to be containing it.

And why is the containing element (the visible grey box) below the controls rather than to the right of them? Yes, your CSS fix would probably resolve this, but shouldn't that be in the book?

I know you didn't write the book, flyinhawaiian, and I do appreciate your reply, but there's something other than positioning on the mapDiv going wrong here.

UPDATE: I've now added that styling, and the grey box (in which the map should be contained, has moved to the bottom-right. So it looks like there's supposed to be some styling attached to #dinnerDiv which hasn't been included in the book.


flyinhawaiian - would I be right in thinking that you found that #mapDiv styling on a downloaded version of the project?

flyinhawaiian October 11th, 2010 03:30 PM

What a major pain huh? You would think that the errata would be updated by now.

Yes, I probably got #mapDiv from another thread or the downloaded complete version. The completed version causes even more confusion because it's constantly changing...they put the map on the left instead of the right, things have been renamed.

I think we can download earlier versions of code. I'll take a look http://nerddinner.codeplex.com/Sourc...ist/changesets to see what I can find.

I'm also comparing my project side-by-side with the finished one to see what's different. That should reveal something.

flyinhawaiian October 11th, 2010 04:30 PM

Ok, finally I got mine fixed. I think there's two ways. Here's one way.
1. Kept the #mapDiv mentioned earlier (in Site.css)

2. Set the id in div tag for the map to "mapDiv"

        <div id="mapDiv">   
            <% Html.RenderPartial("Map", Model.Dinner); %>

3. In Dinnerform.ascx, put the Html.RenderPartial at the top of the fieldset. Since the map is set to float right, everything afterwards will be rendered to the left of it.

4. At this point the Create and Edit should look correct since they both use Dinnerform.ascx.

5. Now fix Details.aspx in the same way, putting the render partial at the top. Open the form in designer. May need to adjust size of MainContent div. Why? There's less fields in the details page so it doesnt extend down as far as the Create/Edit pages. So the map will overlap the bottom of form. Notice they call it details page even though there's not a whole lot of details on that page?

The 2nd way: I havent tried this yet but should work better. Basically do the opposite of the above.

Create #dinnerDiv (instead of #mapDiv) in Site.css with float:left. Use this as the id for the div tags that enclose Dinner. Since dinner is float left, everything after it (the map) should render to the right. This means, the Html.RenderPartial("map") should be last, after all the dinner fields.

ajameson October 11th, 2010 05:18 PM

Thanks for taking a close look, flyinhawaiian.

I've tried both your suggestions (moving RenderPartial to the top with the right-float, and at the bottom with dinnerDiv being left-floated) and while they do move the grey map container around (still not to the perfect location though) my big problem is that the map is still not rendering inside this container.

My entire Map.js file is as you posted up above in the second post. I think the important part is

NerdDinner.MapDivId = 'theMap';
which matches with

<div id="theMap"></div>
on the Map partial.

So the bigger puzzlement is why is the map not rendering inside its container? (I've checked it in Chrome, IE9 and FF.)

flyinhawaiian October 11th, 2010 06:10 PM

Add this to your Site.css When I took this out. I got the same warped out map as you did. So this has got to be it.


    position: relative;
    width: 580px;
    border: solid 1px #bbd1ec;

top of Map.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.Dinner>" %>
<script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2" type="text/javascript"></script>

<div id="theMap" style="width:520px"></div>

All times are GMT -4. The time now is 03:24 PM.

Powered by vBulletin®
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
2013 John Wiley & Sons, Inc.