asp.net-mvc-3google-maps-api-3mvc-editor-templates

How to use Google Maps with a dynamic form


I use an MVC template to allow the user to add multiple addresses. They click a button and this calls my template using jquery and the form is genereated. If they want to add another location they click the button again and another form is setup and so on. This template works fine but I am having some trouble using Google Maps within these templates.

The reason for using Google Maps in this scenario is to provide a nice interface for adding a longitude and latitude for the location you are adding. I'm using class here instead of id's as there could be any number of address areas on the screen.

In my mvc template I have the following markup:

<div class="body">
           <div class="st-form-line">   
                <span class="st-labeltext">Location</span>
                <a href="#" class="GetPosition">Get Latitude &amp; Longitude positions based on above address details.</a>
                <div class="mapContainer" style="display:none;">
                    <p>If the map position is incorrect simply drag the pointer to the correct location.</p>
                    <div class="map" style="width: 450px; height: 350px;"><br/></div>
                </div>
                <div class="clear"></div>
            </div>

            <div class="st-form-line">  
                <span class="st-labeltext">@Html.LabelFor(model => model.Address.Longitude)</span>
                @Html.EditorFor(model => model.Address.Longitude)
                @Html.ValidationMessageFor(model => model.Address.Longitude)
                <div class="clear"></div>
            </div>

            <div class="st-form-line">  
                <span class="st-labeltext">@Html.LabelFor(model => model.Address.Latitude)</span>
                @Html.EditorFor(model => model.Address.Latitude)
                @Html.ValidationMessageFor(model => model.Address.Latitude)
                <div class="clear"></div>
            </div>
</div>

I have the following jquery (this is the bit that's giving me an issue):

$('a.GetPosition').live("click", function (evt) {
        evt.preventDefault();
        $(this).next().show();
        var mapElement = $(this).closest('.body').find('.map');
        var latElement = $(this).closest('.body').find('[id$=__Address_Latitude]');
        var longElement = $(this).closest('.body').find('[id$=__Address_Longitude]');
        showAddress(mapElement, latElement, longElement);
    });

    // Google Map
    var map;
    function showAddress(mapElement, latElement, longElement) {
        var myLatlng = new google.maps.LatLng(40.713956, -74.006653);

        var myOptions = {
            zoom: 8,
            center: myLatlng,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        }
        map = new google.maps.Map(mapElement, myOptions);

        var marker = new google.maps.Marker({
            draggable: true,
            position: myLatlng,
            map: map,
            title: "choose location"
        });

        google.maps.event.addListener(marker, 'click', function (event) {
            latElement.val(event.latLng.lat().toFixed(5));
            longElement.val(event.latLng.lng().toFixed(5));
        });
    }

For now I am just hard coding in the default map position of '40.713956, -74.006653' but I will change this to use a real world address once I can get the map to show correctly.

I know the issue is down to the 'mapElement' but I'm not sure what is wrong. If I comment our the showAddress function call and output some text into mapElement I can see the text so it does appear to have found the right dom loction.

Any ideas I could try here? Thanks for your help.

Thanks,

Rich


Solution

  • So after doing some research it's clear that Google Maps only works if I use an element id. It does not work if I pass it a class.

    To work around this issue I had to dynamically add a div with a unique id when the button to show the map is clicked on.

    My HTML looks like this:

        <div class="body">
            <div class="st-form-line">  
                       <span class="st-labeltext">Location</span>
                       <a href="#" class="GetPosition">Get Latitude &amp; Longitude positions based on above address details.</a>
                       <div class="mapContainer" style="display:none;">
                       <p>If the map position is incorrect simply drag the pointer to the correct location.</p>
                       <p>@Html.LabelFor(model => model.Address.Longitude) @Html.EditorFor(model => model.Address.Longitude) @Html.LabelFor(model => model.Address.Latitude) @Html.EditorFor(model => model.Address.Latitude) <br /> 
                       @Html.ValidationMessageFor(model => model.Address.Longitude) @Html.ValidationMessageFor(model => model.Address.Latitude)</p><br />
                       <div class="clear"></div>
                   </div>
             </div>
         </div>  
    

    The JavaScript looks like this:

    var i = 1;
        $('a.GetPosition').live("click", function (evt) {
            evt.preventDefault();
            // build up address string for map
            var address = //added my address textboxes here - removed for brevity;
            // find the map container and make it visible
            var mapContainer = $(this).next();
            mapContainer.show();
            // create a new map div with unique id
            var mapId = 'map' + i++;
            $("<div></div>").attr("id", mapId).attr("style", "width: 600px; height: 350px;").appendTo(mapContainer);
            // find the lat long textboxes and pass to the Google map function so we can populate with co-ordinates
            var latElement = $(this).closest('.body').find('[id$=__Address_Latitude]');
            var longElement = $(this).closest('.body').find('[id$=__Address_Longitude]');
            showAddress(mapId, address, latElement, longElement);
            // finally hide the button to avoid other map divs being generated
            $(this).hide();
        });
    var map;
    var marker;
    function showAddress(mapId, address, latElement, longElement) {
    
        var map = new google.maps.Map(document.getElementById(mapId), {
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            zoom: 15
        });
    
        var geocoder = new google.maps.Geocoder();
        geocoder.geocode({
            'address': address
        },
           function (results, status) {
               if (status == google.maps.GeocoderStatus.OK) {
                   marker = new google.maps.Marker({
                       draggable: true,
                       position: results[0].geometry.location,
                       map: map
                   });
                   map.setCenter(results[0].geometry.location);
                   latElement.val(results[0].geometry.location.lat().toFixed(5));
                   longElement.val(results[0].geometry.location.lng().toFixed(5));
    
                   google.maps.event.addListener(marker, 'dragend', function (event) {
                       latElement.val(event.latLng.lat().toFixed(5));
                       longElement.val(event.latLng.lng().toFixed(5));
                   });
               }
           });
    }
    

    Obviously there is still some fine tuning required with the above code as error checking needs to be added, etc but hopefully this will help some other people get started with getting Google Maps working with dynamic forms.