Javascript Google Maps For Rails-仅在搜索结果更改时通过ajax更新标记
在stackoverflow的帮助下,我一直在编写一个小应用程序。基本前提很简单,我在网上看到过这种功能:我试图在可搜索/可平移的谷歌地图上绘制一个位置列表。这些位置存储在后端,控制器将这些位置提供给视图。涉及AJAX是因为我不想重新加载整个页面。以下是场景:a)用户通过zipcode=>map搜索位置加载新位置,搜索get发送到服务器,map加载任何标记(如果在设置的半径内有标记),map设置默认缩放级别;b) 用户平移/缩放=>map停留在用户离开的任何位置,使用视口边界框的搜索将发送到服务器并映射结果。地图将在初始加载时默认为西雅图,它尝试的第一件事是对用户进行地理定位 使用gmaps4ails wiki和这个问题答案的修改版本:我已经非常接近了。事实上,它工作起来很顺利。下面是它的外观: 目击控制器.rb search.html.haml search.js.erb 因为_标记似乎在页面的整个生命周期中都保持着它的价值(在设置新标记之前,我们使用它删除旧标记),所以我想我可以简单地创建另一个变量来检查它。然而,即使我认为它应该是真的,它也总是假的 我尝试过的另一件事是在每个搜索请求中重新提交旧的散列作为参数,然后设置一个标志,但这似乎很复杂,字符串/散列/数组操作变得如此混乱,我放弃了。我真的不认为这是最好的方法,但也许我应该这样做Javascript Google Maps For Rails-仅在搜索结果更改时通过ajax更新标记,javascript,ruby-on-rails,ajax,gmaps4rails,Javascript,Ruby On Rails,Ajax,Gmaps4rails,在stackoverflow的帮助下,我一直在编写一个小应用程序。基本前提很简单,我在网上看到过这种功能:我试图在可搜索/可平移的谷歌地图上绘制一个位置列表。这些位置存储在后端,控制器将这些位置提供给视图。涉及AJAX是因为我不想重新加载整个页面。以下是场景:a)用户通过zipcode=>map搜索位置加载新位置,搜索get发送到服务器,map加载任何标记(如果在设置的半径内有标记),map设置默认缩放级别;b) 用户平移/缩放=>map停留在用户离开的任何位置,使用视口边界框的搜索将发送到服务
或者,我是否完全遗漏了什么,应该做些什么?您的问题在于比较两个标记列表,以决定是否应该更新 问题是,尽管isEqual(\uuuuOldMarkers,\uuuuMarkers)确实执行了深入的比较,但列表中的标记实例中可能有一些内容即使对于相同的点(id、时间戳等)也会发生更改。
或者可能只是因为在开始时,
\uuu标记
和\uuu旧标记
都是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;
}
};
这里的\u坐标
和坐标
只是字符串的平面数组,应该快速进行比较,并给出预期结果。在使用
.isEqual
进行比较的顺序中,两个数组都是预先排序的
注意:使用了旧代码,但有差异,但不正确(请参见评论中的讨论)(请注意,我使用的是
.difference
,可能比.isEqual
成本更高,但额外的好处是它独立于返回的标记顺序。)
编辑:哦,当然你现在可以停止发送搜索查询参数中的“oldHash;” 谢谢奥利弗!你的回答看起来不错,有道理,但不起作用。我花了一段时间才弄明白原因!首先是一个输入错误,应该是marker.lat和marker.lng,但这不是你的错,当我发现它时,我认为我已经完成了。但我不是!在困惑地看了很长时间之后,我终于发现问题出在u.差异上。差值([1],null)=1(长度=1),但差值(null,[1])=[blank](长度=0)。结果证明,我需要检查两者:如果(.差(.坐标,_坐标).长度>0)| |(.差(.坐标,坐标).长度>0)),那么尽管差分具有数组元素顺序独立的优点,但差分本身依赖于参数顺序!明天我会考虑是否有更便宜的比较。谢谢Olivier为我们指路!嗯,我刚刚试过并得到:
.difference([1],null)=>[1]
和.difference(null,[1])=>[1]
我打赌您使用的是underline.js,而我个人更喜欢并使用lodash,这是一种更有效的替换下划线的方法。你可以试着用它来代替:)啊,是的,我用的是下划线。我试试洛达斯!尽管如此,如果差异是不同的,那么它们不是彼此的替代品!现在使用lodash,而“null”示例则完全不同。这对于循环很重要,因为当第一次加载页面时,_坐标将为null,因此我需要在开始时放置标记。但是,我仍然需要同时检查(.difference(_坐标,坐标))和(.difference(coordinates,_u坐标)),因为下划线和lo破折号都返回第二个数组中的值,而第一个数组中没有。例如,u.difference([1,2,3],[1]=[2,3],但u.difference([1],[1,2,3])=null。因此,我需要检查这两个条件,以防添加或减去标记。
= 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}});
})
})();
});
(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);
}
})();
(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;
})();
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);
}
};
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;
}
};