Google Maps for Rails — обновление маркеров с помощью AJAX

Я работаю над веб-приложением и использую Ruby on Rails. Наш индекс состоит из карты и поля поиска. Вы можете выполнить поиск местоположения, и карта обновит свои маркеры.

Я хотел бы использовать Ajax, чтобы не обновлять страницу. Поэтому я добавил remote: true в форму, respond_to в контроллер, а новый search.js.erb. My search.js.erb отрисовывает частичное _googlemap.erb, содержащее скрипт для инициализации карты.

Вот моя проблема. Поскольку карта уже существует, это как если бы я хотел создать один и тот же объект дважды, что, конечно, не работает и ужасно. Я хочу обновить только маркеры на карте новыми. Но я не могу найти способ сделать это.

Я видел, что предыдущая версия Gmaps4rails интегрировала способ сделать это ( Gmaps.map.replaceMarkers(your_markers_json_array); ), но сейчас он не работает. Когда я его использую, я получаю эту ошибку: "TypeError: Gmaps.map is undefined". Я пробовал с "handler.replaceMarkers();", но на этот раз у меня "TypeError: handler.replaceMarkers is not a function".

Я новичок в Javascript и Rails, но я хочу улучшить свои знания, и мне действительно нужно продолжить работу с остальной частью этого веб-приложения. Я искал решение везде в Интернете, но напрасно.

Сайт здесь

Подскажите, пожалуйста, как мне это сделать и что я делаю не так?

Спасибо заранее,

Селин

zone_controller.rb

def search
   respond_to do |format|
     format.html.none do
      search_params = params[:zone][:search]
         coordinates = Geocoder.coordinates(search_params).join(",")
         @zones = Zone.search(
           "", { "aroundLatLng" => coordinates, 
                  "aroundRadius" => 500000  #Searches around 500 km
                })
         if coordinates.nil?
           @zones = Zone.search(params[:search])
         elsif @zones.empty?
           @zones = Zone.all
           flash[:error] = "No zone could be found. Please try again."
         end
       build_map(@zones)
     end

     format.js
   end
end

def build_map(array)
  @hash = Gmaps4rails.build_markers(array) do |zone, marker|
    marker.lat zone.latitude
    marker.lng zone.longitude
    marker.json({ title: zone.description, id: zone.id })
    marker.infowindow render_to_string(:partial => "/zones/map_box", locals: { zone: zone })
  end
end

search.html.erb

    <div id="map" style='width: 100%; height: 700px;'>
</div>
  <!-- Beginning Google maps -->

<script type="text/javascript" id="map_script">
   $(document).ready(function() {
     <%= render 'googlemap', hash: @hash %>
   }); // Document ready
</script>

_googlemap.erb

handler = Gmaps.build('Google');
handler.buildMap({ provider: {
    disableDefaultUI: true,
    mapTypeId: google.maps.MapTypeId.TERRAIN
  }, internal: {id: 'map'}
}, function(){
  markers_json = <%= raw hash.to_json %>;
  markers = _.map(markers_json, function(marker_json){

    marker = handler.addMarker(marker_json);
    handler.getMap().setZoom(4);

    _.extend(marker, marker_json);

    marker.infowindow = new google.maps.InfoWindow({
      content: marker.infowindow
    });

    return marker;
  });

  handler.bounds.extendWith(markers);
  handler.fitMapToBounds();
});

search.js.erb

$('#map_script').replaceWith("<%= render 'googlemap', hash: @hash %>");

person Céline Martinet Sanchez    schedule 11.02.2015    source источник
comment
Привет Селин. Я работаю над несколько похожей проблемой; Я ценю как ваш вопрос, так и ответы, которые вы получили. Мне было интересно, не могли бы вы поделиться, как выглядит ваша форма. Кроме того, когда вы изначально загружаете страницу поиска, на контроллер ничего не отправляется, поэтому зоны пусты; вы устанавливаете значение по умолчанию? Я пытаюсь использовать страницу просмотра, чтобы получить местоположение пользователя с помощью геолокации, а затем отправить эту информацию обратно в контроллер, чтобы получить все маркеры рядом, но я не уверен, как это сделать, поскольку порядок потока информации не является традиционным. Кстати, ваша страница выглядит очень здорово.   -  person kindofgreat    schedule 26.02.2015
comment
Здравствуйте, Вы можете найти мою форму здесь. Эта форма находится в index.html.erb и отправляет запрос в /search с параметрами, соответствующими поиску, поэтому она никогда не бывает пустой. Вы можете получить местоположение пользователя (например, с помощью геокодирования IP с Geokit), а затем получить все маркеры рядом и отправить их на просмотр.   -  person Céline Martinet Sanchez    schedule 01.03.2015
comment
Это работает со мной stackoverflow.com/questions/37803378/   -  person alejop120    schedule 29.03.2019
comment
Это работает со мной stackoverflow.com/questions/37803378/   -  person alejop120    schedule 29.03.2019


Ответы (2)


Почему бы вам просто не обновить карту новыми маркерами? Это означает, что вместо повторной визуализации всей карты после каждого поиска просто обновите маркеры на существующей карте, удалив все маркеры и добавив новые.

Я не проверял этот метод, но я думаю, что он должен работать и быть более эффективным:

Создайте файл app/assets/javascript/map.js. Вы можете хранить там свои функции, связанные с картой. Создайте функцию для обновления маркеров вашей карты в этом файле:

карта.js

(function() {
  /* __markers will hold a reference to all markers currently shown
     on the map, as GMaps4Rails won't do it for you.
     This won't pollute the global window object because we're nested
     in a "self-executed" anonymous function */
  var __markers;

  function updateMarkers(map, markersData) 
  {
    // Remove current markers
    map.removeMarkers(__markers);

    // Add each marker to the map according to received data
    __markers = _.map(markersData, function(markerJSON) {
      marker = map.addMarker(markerJSON);
      map.getMap().setZoom(4); // Not sure this should be in this iterator!

      _.extend(marker, markerJSON);

      marker.infowindow = new google.maps.InfoWindow({
        content: marker.infowindow
      });

      return marker;
    });

    map.bounds.extendWith(__markers);
    map.fitMapToBounds();
  };

  // "Publish" our method on window. You should probably have your own namespace
  window.updateMarkers = updateMarkers;
})();

Эту функцию можно использовать для инициализации вашей карты и ее обновления. Поскольку вы не будете (если мой ответ вас удовлетворит) отображать карту дважды, вы можете удалить _google_map.erb и поместить ее содержимое обратно в search.html.erb, но используя только что созданную функцию:

search.html.erb

    <div id="map" style='width: 100%; height: 700px;'>
</div>
  <!-- Beginning Google maps -->

<script type="text/javascript" id="map_script">
   $(document).ready(function() {
       mapHandler = Gmaps.build('Google');
       mapHandler.buildMap({ provider: {
           disableDefaultUI: true,
           mapTypeId: google.maps.MapTypeId.TERRAIN
         }, internal: {id: 'map'}
       }, function(){
         var markersData = <%= raw @hash.to_json %>;
         updateMarkers(mapHandler, markersData);
       });
   }); // Document ready
</script>

Пожалуйста, не забудьте ключевое слово var при объявлении переменных, иначе они станут глобальными, а это плохо ^^
Обратите внимание, что я намеренно оставил mapHandler глобальной переменной: вам понадобится доступ к вашему обработчику для обновления маркеров позже, когда кто-то использует поиск. Это, вероятно, не идеальный вариант, но этот вопрос не о рефакторинге вашего кода, так что давайте оставим его таким.

Итак, теперь мое решение предоставляет вам карту, которая инициализируется заданными маркерами при загрузке страницы. Другими словами, ничего не изменилось!

Однако теперь вам разрешено повторно использовать эту updateMarkers функцию для изменения маркеров, отображаемых на вашей карте. Вот что должен делать ваш search.js.erb скрипт:

search.js.erb

(function() {
  var markersData = <%= raw @hash.to_json %>;
  updateMarkers(mapHandler, markersData);
})();

Вот и все! Надеюсь, это приведет вас к следующему шагу вашего проекта :)

person Olivier Lance    schedule 11.02.2015
comment
Привет Оливье. Спасибо за ответы! Ваш ответ работает очень хорошо, спасибо!! В вашем map.js есть только незначительная опечатка. Это map, а не handler. Единственная проблема, которая у меня есть, касается масштабирования. В начале задает правильно (согласно ответам), но при втором вызове (т.е. используя ajax, а не html) показывает Францию ​​и не увеличивает масштаб. Но с этим я разберусь сам. Спасибо еще раз! - person Céline Martinet Sanchez; 11.02.2015
comment
Привет, Оливье и Селин, спасибо за размещение этого вопроса и ответа. Я пытаюсь что-то подобное, и ваши ответы были очень полезны для меня! Я тоже новичок в рельсах и особенно в javascript. Я реализовал большую часть решения, но у меня есть одна вещь, которую я хотел бы исправить, а именно: когда я панорамирую и масштабирую, я постоянно повторно отправляю поиск, чтобы удалить/добавить маркеры по мере необходимости. Это работает, но если окажется, что те же маркеры уже есть на экране, я не хочу удалять и перерисовывать одни и те же маркеры. Как я могу проверить существующие __markers на новый @hash, который возвращается из поиска? - person kindofgreat; 03.03.2015
comment
Вот что я пробовал, но не работает: я создал новую переменную с именем var __oldHash прямо под var __markers. Затем я сделал _.isEqual(__oldHash, markersData), чтобы увидеть, совпадают ли они, а если нет, то он прошел бы всю функцию UpdateMarkers, и в конце я установил бы __oldHash = markersData. срез(0) . По какой-то причине, если я проверяю isEqual сразу после присваивания, то, конечно, это True. Но наверху, даже когда я думаю, что они должны быть одинаковыми, это всегда неверно... - person kindofgreat; 04.03.2015
comment
Я не уверен, почему __markers будет содержать ссылку на все маркеры на карте, но мой __oldHash, который объявлен в одном и том же месте, пуст каждый раз, когда вызывается функция... Я бы проверил на самом __markers, но я, похоже, не могу этого сделать, так как мне пришлось бы перестраивать карту при создании __newMarkers, чтобы проверить ее, тем самым нарушив цель... - person kindofgreat; 04.03.2015
comment
эй @kindofgreat, я думаю, вам следует начать свой собственный вопрос для этого, чтобы вы могли вставить код и лучше объяснить свою ситуацию;) - person Olivier Lance; 04.03.2015
comment
@ Оливье, я сделаю это, если не смогу понять это сегодня вечером :) Надеялся, что Селин сталкивалась с похожей ситуацией и знала ответ ... Первое, что мне нужно сделать, это научиться использовать инструмент отладки javascript. в Chrome, потому что засорять мой код предупреждениями — это смешно! - person kindofgreat; 04.03.2015
comment
@OlivierLance Я потратил много времени, пытаясь разобраться с этим, но безрезультатно! Если у вас есть минутка, я был бы признателен, если бы вы посмотрели на мой вопрос; Бьюсь об заклад, вы увидите проблему через секунду: stackoverflow.com/questions/28864324/ - person kindofgreat; 04.03.2015

Я пробовал то же самое, но вместо обновления marker вы должны включить карту в partial/placeholder, а затем обновить ее ...

например, это представление, отображающее карту... я обновлю это представление/заполнитель последней картой и маркерами

<div id="map_users_index">
<div id="map" class="map" style="height:relative;">
</div>

в users_controller.rb

   ##take index action or any other action 
    def index
    ##do your stuff and get more users
         respond_to do |format|
           format.html 
           format.js
         end
    end

в index.js.erb

##update the placeholder/partial with new map with new markers

$("#map_users_index").html("<%= escape_javascript(render(:partial => 'index')) %>");

У МЕНЯ ЕСТЬ СОБСТВЕННЫЙ РАБОЧИЙ КОД...ЗДЕСЬ

person Milind    schedule 11.02.2015
comment
Привет, Милинд. Итак, я переместил элемент div map_container в партиал, но теперь карта не хочет инициализироваться. Я не понимаю, потому что @hash дает правильный ответ... Нужно ли мне перемещать javascript в индекс? Но как я могу изменить свои маркеры, если javascript находится в индексе? - person Céline Martinet Sanchez; 11.02.2015
comment
я обновил ответ с кодом. Пожалуйста, проверьте его. Надеюсь, это поможет вам :)... @CélineMartinetSanchez - person Milind; 15.02.2015