egeloen / IvoryGoogleMapBundle

Google Map API v3 integration for your Symfony2 project.
https://github.com/egeloen/ivory-google-map
MIT License
217 stars 152 forks source link

HOWTO use asynchronous map with Ajax ? #103

Closed emmanuel-tesson closed 10 years ago

emmanuel-tesson commented 10 years ago

hi i want to use the asynchronous loading map with your bundle. But i have no idea to do that.

To try i wrote a function in the controller :

    /**
 * @Route("/show/showmap/update", name="onet_map_search")
 * @Method("POST")
 */
 public function updateMapAjaxAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();
    if ($request->isXmlHttpRequest())
    {
        $division = '';
        $division = $request->request->get('division');
        if($division !='')
        {
            $agencies = $em->getRepository('OnetRealEstateBundle:Agency')->findByDivision($division);
        }else{
            $agencies = $em->getRepository('OnetRealEstateBundle:Agency')->findAll();
        }
    }else{
        $agencies = $em->getRepository('OnetRealEstateBundle:Agency')->findAll();
    }
    $dataSend = $this->getMap($agencies,'1100px','600px');
    $response = new JsonResponse();
    $response->setData(json_encode($dataSend));
    $response->setCallback('load_ivory_google_map');
    return $response;
}

the function "getMap" is responsible to define the map object :

      private function getMap($entities,$width="300px",$height="300px"){
        /** @var Ivory\GoogleMapBundle\Model\Map
         * LA CARTE
         * */
        $map = $this->get('ivory_google_map.map');
        $map->setStylesheetOptions(array('width'=>$width,'height'=>$height));
        // Enable the auto zoom flag
        $map->setAutoZoom(true);
        // Sets the bound coordinates
        $map->setBound(-2.1, -3.9, 2.6, 1.4, true, true);
        //Asynchronous Map Ajax
        $map->setAsync(true);

        //MarkerCluster
        $markerCluster = $map->getMarkerCluster();
        $markerCluster->setType(MarkerCluster::MARKER_CLUSTER);
        $markerCluster->setType('marker_cluster');
        $markerCluster->setOption('gridSize', 50);
        $markerCluster->setOption('maxZoom', 15);

        foreach($entities as $entity){
            if($geocode = $this->geoCode($entity,false)) {
                $marker = new Marker();// Create a marker
                $marker->setPosition($geocode);// Position the marker
                $infoWindow = new InfoWindow();
                $infoWindow->setContent($entity->getName().'<br>'.$entity->getPostalAddress());
                $marker->setInfoWindow($infoWindow);
                $marker->setIcon($this->setOwnerMarker($entity));
                // Add the marker to the map
                $map->addMarker($marker);
            }
        }           
        return $map;
}

My twig view file is :

                {% extends '::base.html.twig' %}

 {% block javascripts %}
<script>
$(".loading").hide();
$("#mapSearchForm").submit(function(){ 
$(".loading").show();
var division = $("#mapsearch_division").val();
var DATA = 'division=' + division;
$.ajax({
    type: "POST",
    url: "{{ path('onet_map_search')}}",
    async:true,
    jsonpCallback: 'load_ivory_google_map',
    data: DATA,
    cache: false,
    success: function(data){
        console.log(data);
       $(".loading").hide();
    },
    error: function() 
    {
        console.log('failed');
    }
   });    
return false;
});
</script>
{% endblock %}
 {% block body -%}
 {{ google_map_css(map) }}
<h1>{{ 'Agency Map'|trans }}</h1>
 <div class="row">
    <form id="mapSearchForm" action="{{ path('onet_map_search') }}" method="post">
        {{ form_widget(mapSearchForm) }}
        <input type="submit" value="{{ 'filter'|trans }}"/>
    </form>
</div>
<div class="row" >{{ google_map_container(map) }}</div>
<div class="row">
    <ul>
    <li>
        <a href="{{ path('agency_new') }}">
            {{ 'Create a new entry'|trans }}
        </a>
     </li>
     <li>
         <a href="{{ path('agency_newcsv') }}">
            {{ 'Import from CSV'|trans }}
         </a>
      </li>
   </ul>
   </div>
    {{ google_map_js(map) }}
  {% endblock %}

And finally the MapSerachForm.php file for the initial form :

   <?php
       namespace Onet\RealEstateBundle\Form;
      use Symfony\Component\Form\AbstractType;
      use Symfony\Component\Form\FormBuilderInterface;
   class MapSearchForm extends AbstractType
    {
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('division','choice',array(
                'choices'=>array(
                        ''=>'Tous',
                        'ONET PROPRETE MULTISERVICES'=>'Pôle services',
                        'ONET TECHNOLOGIES' => 'Pôle technologies',
                        'ONET SECURITE' => 'Pôle sécurité',
                        'AXXIS RESSOURCES' => 'Axxis Ressources'
                        ),
                'required'=> false
                )
            );
     }
        public function getName()
       {
    return 'mapsearch'; 
       }
     }

but when i'm executing the form, i have just : load_ivory_google_map("{}");

Thanks in advance if you could help me

egeloen commented 10 years ago

The problem here is you are trying to json_encode a map and then manage the map loading yourself via a callback. Basically, the library does not work this way.

To load a map asynchronously, your just need to merge the generated html/css/js in your page and then everything will be loaded asynchronously for you. Async here means the the google loader will load the google map lib async and then will load your map when everything is ready.

emmanuel-tesson commented 10 years ago

Hi Eric Thanks a lot for you quick answer

I try this week end to change the code to respect your explanation. I think I’m not so far away from solution, but i probably have a little problem to the end. i’m ok with the request, but not for the response object to merge… I test the response, it seems to be good (i have the callback function) but the map centered to 0,0 and no markers change. I think i forgot something in the JS Success ajax Function ???

If you can help me, i let you below my coding.

To begin, my twig view file is :

{% extends '::base.html.twig' %} {% block javascripts %}

{{ google_map_js(map) }} {% endblock %}

{% block body -%}

Store list

<div class="row">
    <div class=col-md12">
    <form id="form_recherche" action="{{ path('mystil_store_search') }}" method="post"  {{ form_enctype(form) }} >
        {{ form_widget(form) }}
        <input type="submit" value="{{ 'Rechercher'|trans }}" />
    </form>
    </div>
</div>

<div class="row">
    <div class=col-md12">
        {{ google_map_container(map) }}
    </div>
</div>

<div class="row">
    <div class=col-md12">
    <table class="records_list">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Address</th>
            <th>Zipcode</th>
            <th>City</th>
            <th>Country</th>
            <th>Longitude</th>
            <th>Latitude</th>
            <th>Activities</th>
            <th>Actions</th>
        </tr>
    </thead>
    <tbody>
    {% for entity in entities %}
        <tr>
            <td><a href="{{ path('store_show', { 'id': entity.id }) }}">{{ entity.id }}</a></td>
            <td>{{ entity.name }}</td>
            <td>{{ entity.address }}</td>
            <td>{{ entity.zipcode }}</td>
            <td>{{ entity.city }}</td>
            <td>{{ entity.country }}</td>
            <td>{{ entity.longitude }}</td>
            <td>{{ entity.latitude }}</td>
            <td>{{ entity.activities }}</td>
            <td>
            <ul>
                <li>
                    <a href="{{ path('store_show', { 'id': entity.id }) }}">show</a>
                </li>
                <li>
                    <a href="{{ path('store_edit', { 'id': entity.id }) }}">edit</a>
                </li>
            </ul>
            </td>
        </tr>
    {% endfor %}
    </tbody>
</table>
    </div>
</div>

    <ul>
    <li>
        <a href="{{ path('store_new') }}">
            Create a new entry
        </a>
    </li>
</ul>
{% endblock %}

My « Update Controller function » is :

/**
 * 
 * 
 * @Route("/updatemap/",name="mystil_store_search")
 * 
 * @Method("POST")
 */
public function updateGoogleMap()
{

    $request = $this->getRequest();

    $callback = $request->get('callback');

    if($request->isXmlHttpRequest())
    {
        $em = $this->getDoctrine()->getManager();
        $entities = $em->getRepository('MystilStoreNetworkBundle:Store')->findBy(array(),array(),1,0);

        $mapHelper = new MapHelper();

        $map = $this->getMap($entities);
        $map->setCenter(43.1787602, 5.5946439, true);
        $map->setMapOption('zoom', 3);

        $mapHelperHtml = $mapHelper->renderHtmlContainer($map);// This function renders an html div block with the HTML container ID, the width & the height configured:
        $mapHelperJs  = $mapHelper->renderJsContainer($map);//This function renders an html javascript block with all code needed for displaying your map.

        $response = new JsonResponse();
        $response->setCallback($callback);
        $response->setData($mapHelperJs);

    }

    return $response;
}

Best Regards

Le 7 févr. 2014 à 20:53, Eric GELOEN notifications@github.com a écrit :

The problem here is you are trying to json_encode a map and then manage the map loading yourself via a callback. Basically, the library does not work this way.

To load a map asynchronously, your just need to merge the generated html/css/js in your page and then everything will be loaded asynchronously for you. Async here means the the google loader will load the google map lib async and then will load your map when everything is ready.

— Reply to this email directly or view it on GitHub.

emmanuel-tesson commented 10 years ago

@egeloen could you explain me how to merge the js generated in my page ?

i'm actually trying this, but it's not ok for now : $.ajax({ type: "POST", url: "{{ path('onet_map_search')}}", async:true, jsonpCallback: 'load_ivory_google_map', dataType: 'jsonp', data: DATA, cache: false, success: function(data,textStatus,jqXHR){

        console.log(data);
        console.log(textStatus);
        console.log(jqXHR);

        $('#myscriptjs script.first').html("function load_ivory_google_map() {" + jqXHR.responseJson + "}");

//      load_ivory_google_map(data);

       $(".loading").hide();
    },
    error: function() 
    {
        console.log('failed');
    }
});  
egeloen commented 10 years ago

To answer your question, your controller should look like:

$html = $mapHelper->render($map);

return new Response($html);
// or if you prefer a json response
return new JsonResponse(array('html' => $html));

Then, your javascript:

$.ajax({
    type: "POST",
    url: "{{ path('onet_map_search')}}",
    data: myData,
    success: function(data) {
        $('#my-map').html(data);
        // or if your use the json response
        $('#my-map').html(data.html);
    }
});
emmanuel-tesson commented 10 years ago

@egeloen thanks a lot !!! It's finally so simple ... in my first tries, i would like to update only the js script, and i don't understand how to do this. Your solution change all the content css and html and Js.

Could i abuse just one time...???... When i load the ajax script, and reload the map, I lost the centering of the map. In my config.yml, i wrote :

      # Center coordinate of the map
     # If the autozoom flag is enabled, the center is not used
    center:
        longitude: 43.1787602
        latitude: 5.5946439
        no_wrap: true

and when i create the map object

    $map = $this->getMap($agencies,'1100px','600px');
    $map->setCenter(43.1787602, 5.5946439, false);
    $map->setMapOption('zoom', 3);

    $mapHelper = new MapHelper();
    $html = $mapHelper->render($map);
    return new JsonResponse(array('html'=>$html)); 

but on load, the map is centering on 0,0 ??

egeloen commented 10 years ago

Have you disable the map autozoom? https://github.com/egeloen/ivory-google-map/blob/master/doc/usage/map.md#standard-center-coordinate--zoom

emmanuel-tesson commented 10 years ago

@egeloen No i did not disable this option !.. i tried and all seems to be ok when i specified the center location with (and the same parameter in the config.yml) : $map->setCenter(43.1787602, 5.5946439, false); $map->setMapOption('zoom', 4); $map->setAutoZoom(false);

but when i tried to use the auto zoom, the center is always 0,0

egeloen commented 10 years ago

Do you append overlay on your map? marker, etc...?

emmanuel-tesson commented 10 years ago

Yes, i add some markers, InfoWindows, MarkerClusters. My private getMap Function in the controller add all (It's the code with manual centering) :

     /**get Map **/
private function getMap($entities,$width="300px",$height="300px"){

        /** @var Ivory\GoogleMapBundle\Model\Map
         * LA CARTE
         * */
        $map = $this->get('ivory_google_map.map');
//      $map->setCenter(47.74, 2.37, true);
//      $map->setMapOption('zoom', 4);
        $map->setStylesheetOptions(array('width'=>$width,'height'=>$height));

        // Enable the auto zoom flag
        $map->setAutoZoom(false);

        // Sets the bound coordinates
        $map->setBound(-2.1, -3.9, 2.6, 1.4, true, true);
        //Asynchronous Map Ajax
        $map->setAsync(true);

        $this->getMapTypeControl($map);
        $this->getOverviewMapControl($map);
        $this->setMarkerCluster($map);
        $this->addMarkers($map,$entities);
        $this->getPanControl($map);
        $this->getRotateControl($map);
        $this->getScaleControl($map);
        $this->getStreetViewControl($map);
        $this->getZoomControl($map);

    //  $this->addKmlLayer($map, $this->container->get('templating.helper.assets')->getUrl('bundles/onetrealestate/kml/whc-fr.kml'));
    //  $this->addKmlLayer($map, "http://gmaps-samples.googlecode.com/svn/trunk/ggeoxml/cta.kml");
    //  $this->addKmlLayer($map, $this->container->get('templating.helper.assets')->getUrl('bundles/onetrealestate/kml/cta.kml'));
        return $map;
}

i decided to comment the code with addKmlLayer, because i had some probleme with kml local file, but i thnik that i could find the solution after.

egeloen commented 10 years ago

What is your marker cluster?

emmanuel-tesson commented 10 years ago

i used your example to add marker cluster :

 private function setMarkerCluster($map)
{

    //MarkerCluster
    $markerCluster = $map->getMarkerCluster();
    $markerCluster->setType(MarkerCluster::MARKER_CLUSTER);
    $markerCluster->setType('marker_cluster');
    $markerCluster->setOption('gridSize', 50);
    $markerCluster->setOption('maxZoom', 15);

}
egeloen commented 10 years ago

Basically, you're using the google map utility marker cluster and not the default marker cluster. The autozoom feature is not compatible with this marker cluster :s

KrzysiekOwczarek commented 10 years ago

Hi,

I don't want to repeat topics, so I will use this one.

I reload my map via onlick event using jquery $.post function. After reloading my map appears with perfectly refreshed state but with not working autoZoom. Procedure is pretty straight forward, here are some bits of code:

$.post("{{ path('trucks_map') }}", { keyword: $('#search_truck').val(), type: type }, function(data){ $('#map_container').html(data); });

KrzysiekOwczarek commented 10 years ago

function binded with route 'trucks_map':

public function prepareMapAction(Request $request) {

    $lon = $request->request->get('lon');
    $lat = $request->request->get('lat');
    $keyword = $request->request->get('keyword');
    $type = $request->request->get('type');

    if(!$lon)
        $lon = null;

    if(!$lat)
        $lat = null;

    if(!$keyword)
        $keyword = null;

    if(!$type)
        $type = null;

    $em = $this->getDoctrine()->getManager();

    $map = $this->mapInit('map_container');
    $coordinates = $em->getRepository('AutoBundle:Truck')->getCoordinates($keyword, $type);

    $this->mapAddMarkers($map, $coordinates);

    return $this->render('AutoBundle:Trucks:map.html.twig', array(
        'map' => $map,
    ));
}

First occurance via twig:

{{ render(controller('AutoBundle:Trucks:prepareMap')) }}

Any ideas?

Thanks in advance.

egeloen commented 10 years ago

@KrzysiekOwczarek If you still have your issue please reopen a new one :)

Closing as the main issue has been fixed here.

marisgithub commented 10 years ago

Hi,

I have question regarding your answer in "Feb 11" about refreshing map with ajax. With your example, that you provided, everything is working just fine, but is it possible to "refresh" only markers and not replace whole HTML?