GoogleMapsForRails-只有在搜索结果发生变化时才通过ajax更新标记
Google Maps For Rails - updating markers via ajax only when search result changes
在stackoverflow的帮助下,我一直在编写一个小型应用程序。基本前提很简单,我在网上看到过这种功能:我试图在一个可搜索/可平移的谷歌地图上绘制一个位置列表。这些位置存储在后端,控制器将这些位置提供给视图。AJAX是因为我不想重新加载整个页面。以下是场景:a)用户通过邮政编码搜索位置=>地图加载新位置,将搜索结果发送到服务器,如果在设置的半径内有任何标记,则地图加载任何标记,地图设置默认缩放级别;b) 用户平移/缩放=>地图停留在用户离开的任何位置,带有视口边界框的搜索将发送到服务器,并映射结果。地图在初始加载时将默认为西雅图,它尝试的第一件事是对用户进行地理定位。。。
使用gmaps4ails-wiki和这个问题的答案的主要修改版本:GoogleMapsforRails-使用AJAX更新标记,我已经非常接近了。事实上,它只是有一个问题。以下是它的样子:
sightings_controller.rb
def search
if params[:lat]
@ll = [params[:lat].to_f, params[:lng].to_f]
@sightings = Sighting.within(5, origin: @ll).order('created_at DESC')
@remap = true
elsif search_params = params[:zipcode]
geocode = Geokit::Geocoders::GoogleGeocoder.geocode(search_params)
@ll = [geocode.lat, geocode.lng]
@sightings = Sighting.within(5, origin: @ll).order('created_at DESC')
@remap = true
elsif params[:bounds]
boundarray = params[:bounds].split(',')
bounds = [[boundarray[0].to_f, boundarray[1].to_f], [boundarray[2].to_f, boundarray[3].to_f]]
@ll = [params[:center].split(',')[0].to_f, params[:center].split(',')[1].to_f]
@sightings = Sighting.in_bounds(bounds, origin: @ll).order('created_at DESC')
@remap = false
else
search_params = '98101'
geocode = Geokit::Geocoders::GoogleGeocoder.geocode(search_params)
@ll = [geocode.lat, geocode.lng]
@sightings = Sighting.within(5, origin: @ll).order('created_at DESC')
@remap = true
end
@hash = Gmaps4rails.build_markers(@sightings) do |sighting, marker|
marker.lat sighting.latitude
marker.lng sighting.longitude
marker.name sighting.title
marker.infowindow view_context.link_to("sighting", sighting)
end
respond_to do |format|
format.html
format.js
end
end
search.html.haml
= form_tag search_sightings_path, method: "get", id: "zipform", role: "form", remote: true do
= text_field_tag :zipcode, params[:zipcode], size: 5, maxlength: 5, placeholder: "zipcode", id: "zipsearch"
= button_tag "Search", name: "button"
%input{type: "button", value: "Current Location", onclick: "getUserLocation()"}
#locationData
.sightings_map_container
.sightings_map_canvas#sightings_map_canvas
#sightings_container
- content_for :javascript do
%script{src: "//maps.google.com/maps/api/js?v=3.13&sensor=false&libraries=geometry", type: "text/javascript"}
%script{src: "//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js", type: "text/javascript"}
:javascript
function getUserLocation() {
//check if the geolocation object is supported, if so get position
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(setLocation);
}
else {
document.getElementById("locationData").innerHTML = "Sorry - your browser doesn't support geolocation!";
}
}
function setLocation(position) {
//build text string including co-ordinate data passed in parameter
var displayText = "Latitude: " + position.coords.latitude + ", Longitude: " + position.coords.longitude;
//display the string for demonstration
document.getElementById("locationData").innerHTML = displayText;
//submit the lat/lng coordinates of current location
$.get('/sightings/search.js',{lat: position.coords.latitude, lng: position.coords.longitude});
}
// build maps via Gmaps4rails
handler = Gmaps.build('Google');
handler.buildMap({
provider: {
},
internal: {
id: 'sightings_map_canvas'
}
},
function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(setLocation);
}
var json_array = #{raw @hash.to_json};
var latlng = #{raw @ll};
resetMarkers(handler, json_array);
resetMap(handler, latlng);
// listen for pan/zoom and submit new coordinates
(function gmaps4rails_callback() {
google.maps.event.addListener(handler.getMap(), 'idle', function() {
var bounds = handler.getMap().getBounds().toUrlValue();
var center = handler.getMap().getCenter().toUrlValue();
$.get('/sightings/search.js',{bounds: bounds, center: center, old_hash: #{raw @hash.to_json}});
})
})();
});
search.js.erb
(function() {
var json_array = <%= raw @hash.to_json %>;
if (<%= @remap %>) {
var latlng = <%= raw @ll %>;
resetMarkers(handler, json_array);
resetMap(handler, latlng);
}
else {
resetMarkers(handler, json_array);
}
})();
map.js
(function() {
function createSidebarLi(json) {
return ("<li><a>" + json.name + "</a></li>");
};
function bindLiToMarker($li, marker) {
$li.on('click', function() {
handler.getMap().setZoom(18);
marker.setMap(handler.getMap()); //because clusterer removes map property from marker
google.maps.event.trigger(marker.getServiceObject(), 'click');
})
};
function createSidebar(json_array) {
_.each(json_array, function(json) {
var $li = $( createSidebarLi(json) );
$li.appendTo('#sightings_container');
bindLiToMarker($li, json.marker);
});
};
function clearSidebar() {
$('#sightings_container').empty();
};
function clearZipcode() {
$('#zipform')[0].reset();
};
/* __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 resetMarkers(handler, json_array) {
handler.removeMarkers(__markers);
clearSidebar();
clearZipcode();
if (json_array.length > 0) {
__markers = handler.addMarkers(json_array);
_.each(json_array, function(json, index){
json.marker = __markers[index];
});
createSidebar(json_array);
}
};
function resetMap(handler, latlng) {
handler.bounds.extendWith(__markers);
handler.fitMapToBounds();
handler.getMap().setZoom(12);
handler.map.centerOn({
lat: latlng[0],
lng: latlng[1]
});
}
// "Publish" our method on window. You should probably have your own namespace
window.resetMarkers = resetMarkers;
window.resetMap = resetMap;
})();
这就是问题所在,这与这个特定的例子有关,也与我对javascript(我是新手)变量如何工作的误解有关。当用户平移和缩放,但搜索结果相同时,我宁愿不调用"resetMarkers"函数,而宁愿不调用地图。地图目前总是重置markers/边栏等,这会导致屏幕上的标记闪烁。
我试过几种不同的版本,但都不起作用。在map.js:中
var __markers;
var __oldmarkers;
function resetMarkers(handler, json_array) {
if(!(_.isEqual(__oldmarkers, __markers))) {
handler.removeMarkers(__markers);
clearSidebar();
clearZipcode();
if (json_array.length > 0) {
__markers = handler.addMarkers(json_array);
_.each(json_array, function(json, index){
json.marker = __markers[index];
});
createSidebar(json_array);
}
__oldmarkers = __markers.slice(0);
}
};
由于__markers似乎在页面的整个生命周期中都保持其值(我们在设置新标记之前使用它来删除旧标记),我想我可以简单地创建另一个变量来检查它。然而,即使我认为它应该是真的,它也总是错的。
我尝试过的另一件事是在每个搜索请求中重新提交旧的哈希作为参数,然后设置一个标志,但这似乎很复杂,字符串/哈希/数组操作变得非常混乱,我放弃了。我真的不认为这是最好的方法,但也许我应该这样做?
或者,是不是有什么我完全错过了,应该做的事情?
您的问题在于比较两个标记列表来决定是否应该更新。
问题是,尽管_.isEqual(__oldmarkers, __markers)
确实执行了深入的比较,但即使对于相同的点(id、时间戳…),列表中的Marker实例中也可能会发生变化。
或者,这可能只是因为在一开始,__markers
和__oldMarkers
都是null
,因此相等,这意味着你永远不会进入if
块。
无论如何,我认为这里的深度比较可能会变得过于昂贵。相反,我要做的是比较容易比较的东西,比如每组标记的平面坐标列表。
类似这样的东西:
var __markers, __coordinates = [];
function resetMarkers(handler, json_array)
{
var coordinates = _.map(json_array, function(marker) {
return String(marker.lat) + ',' + String(marker.lng);
});
if(_.isEqual(__coordinates.sort(), coordinates.sort()))
{
handler.removeMarkers(__markers);
clearSidebar();
clearZipcode();
if (json_array.length > 0)
{
__markers = handler.addMarkers(json_array);
_.each(json_array, function(json, index){
json.marker = __markers[index];
});
createSidebar(json_array);
}
__coordinates = coordinates;
}
};
这里__coordinates
和coordinates
只是String的平面数组,应该快速比较它们并给出预期的结果
按照使用_.isEqual
进行比较的顺序,两个数组都是预先排序的。
NB:旧代码使用了_.difference,但这是不正确的(请参阅评论中的讨论)
(注意,我使用的是_.difference
,可能比_.isEqual
更贵,但额外的好处是独立于返回的标记顺序。)
edit:哦,当然你现在可以停止在搜索查询params中发送"oldHash"了;)
- Ajax更新面板随机错误'PRM_MissingPanel'
- AJAX更新面板不;t工作.请帮我找出我错在哪里.
- Ajax更新表数据,但不重复顺序
- 通过javascript确认AJAX更新数据库
- 使用AJAX更新Shopify中的购物车编号
- 为什么在ASP.NET Ajax更新面板中执行任何操作后javascript元素都停止工作
- 创建使用 ajax 更新 iframe 的链接
- 谷歌图表错误'不是数组'关于Ajax更新
- GoogleMapsForRails-只有在搜索结果发生变化时才通过ajax更新标记
- ajax更新后jQuery列表刷新
- 使用PHP和AJAX更新select下拉选项
- 可以使用ajax更新页面
- 表单在每个页面中 - 仅在特定页面上处理它 - 如果当前不在页面上 - 重定向 - 否则 - 使用 AJAX 更新数据
- 使用jquery ajax更新数据库;不起作用
- JSF在Ajax更新后调用javascript函数
- 通过ajax更新HTML块内容
- jQuery日期选择器在Firefox和IE的MVC部分视图上进行ajax更新后无法工作
- 在第三次onPageClick事件后,使用twbspagination插件和AJAX更新JS分页
- 使用AJAX更新MySQL数据库
- 使用javascript将span更改为input(viceversa),然后AJAX更新输入