使用KnockoutJS重置Google地图标记的过滤器

时间:2017-12-08 03:51:28

标签: javascript google-maps google-maps-api-3 knockout.js

我正在尝试使用Google Maps API和KnockoutJS创建可过滤体育场馆的地图。除了用户键入输入并删除所有字符(搜索栏中没有字符......所有标记应返回到可见性)之外,标记过滤得很好。我已经尝试了几种不同的东西来使所有的标记在这种情况下可见,包括使用setVisible函数的各个地方,但是,似乎无法确定解决方案。我知道这可能很简单,但我有一个障碍。

非常感谢任何帮助。

这是我的代码:

HTML:

<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='css/bootstrap-theme.min.css'>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet">
<link rel='stylesheet' href='css/main.css'>
</head>

<body>
  <container>
    <nav>
      <ul>
        <div id='menu'>
        <li>
          <i class='material-icons'>&#xE5D2</i>
        </li>
      </div>
      </ul>
    </nav>
    <div id='overmap'>
    <div id='filter'>
      <div class='filter-bar'>
        <h2>Search Pittsburgh Sports Venues</h2>
        <input id='userFilter' data-bind="value:userSearch, valueUpdate: 'input'" placeholder="Type a venue name..."/>
      </div>
     <ul id="list" data-bind="foreach: filterVenues">
      <a href='#'>   <li>
      <h3 data-bind="text: name, click: $parent.setVenue"></h3>
    </li></a>


      </ul>
      </div>
    </div>
</div>
    <div id='map'>
    </div>
  </container>



  <script type='text/javascript' src='js/jquery-3.2.1.min.js'></script>
  <script type='text/javascript' src='js/knockout-3.4.2.js'></script>
  <script type='text/javascript' async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA_WObUiYD7YpoYufR84re1LZHAJeAGXkY&v=3&callback=initMap">
  </script>
  <script type='text/javascript' src='js/app.js'></script>
  <script type='text/javascript' src='js/menu.js'></script>
</body>
</html>

app.js:

var map;
var marker;
var infowindow;
var wikiURL;
var text;
var venueInfo;
var markers = [];
var markerNames = [];
var wikiURLs = [];
var venueArray = [];
var blackMarker = ('https://www.google.com/mapfiles/marker_black.png');
var yellowMarker = ('https://www.google.com/mapfiles/marker_yellow.png');
var currentMarker;
var currentVenue;
var venueList;
var filterVenues;
var clicker;
var venueMatch;
var venue;
var i;
var userSearch;
var setVenue;
var clickedVenue;

//The Model - Pro/Collegiate Stadiums in PGH, Pa.
var venues = [
  {
    name: "PNC Park",
    lat: 40.446855,
    lng: -80.0056666,
    marker: '',
    info: ''
  },
  {
    name: "Heinz Field",
    lat: 40.4466765,
    lng: -80.01576,
    marker: '',
    info: ''
  },
  {
    name: "PPG Paints Arena",
    lat: 40.439593,
    lng: -79.989338,
    marker: '',
    info: ''
  },
  {
    name: "Highmark Stadium",
    lat: 40.4362358,
    lng: -80.00959209999999,
    marker: '',
    info: ''
  },
  {
    name: "Peterson Events Center",
    lat: 40.443828,
    lng: -79.962283,
    marker: '',
    info: ''
  }
];


function ajaxCall(i){
  var venue = venues[i];
  wikiURL = 'http://en.wikipedia.org/w/api.php?action=opensearch&search=' +venue.name+ '&format=json&callback=wikiCallback';


  wikiURLs.push(wikiURL);

  $.ajax ({
      url: wikiURL,
      dataType: "jsonp",
      success: function(data){
            text = data[2];
            venues[i].info = text[0];


              if (venue.info === undefined) {
                venue.info = 'Whoops! Our data never showed up. Check out '+venue.name+' on Wikipedia for more!';
              }
              marker = new google.maps.Marker({
                position: {lat: venue.lat, lng: venue.lng},
                icon: blackMarker,
                map: map,
                name: venue.name,
                draggable: false,
                content: '<h2>'+venue.name+'</h2><p>'+venue.info+'</p>',
                visible: true
              });



                markers.push(marker);
                venue.marker = marker;
                markerNames.push(marker.name);



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



                marker.addListener('click', function(){
                  infowindow.setContent(this.content);
                  infowindow.open(map, this);
                  for (var i = 0; i<markers.length; i++){
                    markers[i].setIcon(blackMarker);
                  }
                  this.setIcon(yellowMarker);

                });
        }
      });

  }








//capturing locations and names in arrays as we iterate through createMarker function
//marker creator

function createMarker(venue){
for (var i=0; i < venues.length; i++){
ajaxCall(i);
}
}




var viewmodel = function() {


var self = this;


self.userSearch = ko.observable("");
self.venues = ko.observableArray(venues);
this.currentVenue = ko.observable(self.venues()[0]);
this.setVenue = function(clickedVenue) {
  self.currentVenue(clickedVenue);
  google.maps.event.trigger(clickedVenue.marker, 'click');
};


self.filterVenues = ko.computed(function(venue) {
  var search = self.userSearch().toLowerCase();
  if (!search) {
    return self.venues();

  } else {
    return ko.utils.arrayFilter(self.venues(), function(venue) {
    if (venue.name.toLowerCase().indexOf(search) !== -1) {
      venue.marker.setVisible(true);
      return true;
    } else {
      venue.marker.setVisible(false);
      return false;

    }


    });
    }


});
};







//Map Initializer

function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 40.446855, lng: -80.0056666},
    zoom: 14,
    mapTypeId: 'satellite'
  });


createMarker();

ko.applyBindings(new viewmodel());



}

1 个答案:

答案 0 :(得分:0)

一旦过滤器为空,显示所有标记的最简单方法可能是将filterVenues observable更新为:

 self.filterVenues = ko.computed(function (venue) {
    var search = self.userSearch().toLowerCase();
    return ko.utils.arrayFilter(self.venues(), function (venue) {
        if (venue.name.toLowerCase().indexOf(search) !== -1) {
            venue.marker.setVisible(true);
            return true;
        } else {
            venue.marker.setVisible(false);
            return false;
        }
    });
});

2)提供的例子还有一个缺陷。由于标记是异步添加,因此无法保证在显示地图时,每个场地都可以使用所有标记。

我建议改变初始化过滤器控件的方式,特别是绑定过滤器控件一旦所有标记初始化

initMarkers(map, infowindow)
    .done(function () {
        ko.applyBindings(new viewmodel());
        console.log("map is ready");
    });

我们引入了一个初始化标记的函数:

function initMarkers(map, infowindow) {
    var promises = [];
    for (var i = 0; i < venues.length; i++) {

        var request = (function (venue) {
            return loadDetails(venue)
                .then(function (data) {
                    venue.info = data[2];
                    if (venue.info === undefined) {
                        venue.info = 'Whoops! Our data never showed up. Check out ' + venue.name + ' on Wikipedia for more!';
                    }
                    createMarker(map, infowindow, venue);
                });
        })(venues[i]);
    }
    promises.push(request);
    return $.when.apply(null, promises);
}

和使用CORS而不是JSONP(没有callback参数)加载维基百科数据的函数:

function loadDetails(venue) {
    var wikiURL = 'https://en.wikipedia.org/w/api.php?&origin=*&action=opensearch&search=' + venue.name + '&format=json';
    return $.ajax({
        url: wikiURL
    });
}

3)最后但并非最不重要的是,不需要为每个标记创建信息窗口的实例,而是可以创建单个实例。

下面提供了修改示例

var blackMarker = ('https://www.google.com/mapfiles/marker_black.png');
var yellowMarker = ('https://www.google.com/mapfiles/marker_yellow.png');
//The Model - Pro/Collegiate Stadiums in PGH, Pa.
var venues = [
    {
        name: "PNC Park",
        lat: 40.446855,
        lng: -80.0056666,
        marker: '',
        info: ''
    },
    {
        name: "Heinz Field",
        lat: 40.4466765,
        lng: -80.01576,
        marker: '',
        info: ''
    },
    {
        name: "PPG Paints Arena",
        lat: 40.439593,
        lng: -79.989338,
        marker: '',
        info: ''
    },
    {
        name: "Highmark Stadium",
        lat: 40.4362358,
        lng: -80.00959209999999,
        marker: '',
        info: ''
    },
    {
        name: "Peterson Events Center",
        lat: 40.443828,
        lng: -79.962283,
        marker: '',
        info: ''
    }
];


function loadDetails(venue) {
    var wikiURL = 'https://en.wikipedia.org/w/api.php?&origin=*&action=opensearch&search=' + venue.name + '&format=json';
    return $.ajax({
        url: wikiURL
    });
}

function createMarker(map, infowindow, venue) {
    var marker = new google.maps.Marker({
        position: { lat: venue.lat, lng: venue.lng },
        icon: blackMarker,
        map: map,
        name: venue.name,
        draggable: false,
        content: '<h2>' + venue.name + '</h2><p>' + venue.info + '</p>',
        visible: true
    });
    //console.log(marker.name);

    marker.addListener('click', function () {
        if (infowindow.prevMarker)
            infowindow.prevMarker.setIcon(blackMarker);
        infowindow.setContent(this.content);
        infowindow.open(map, this);
        this.setIcon(yellowMarker);
        infowindow.prevMarker = this;
    });
    venue.marker = marker;
    return marker;
}

function initMarkers(map, infowindow) {
    var promises = [];
    for (var i = 0; i < venues.length; i++) {

        var request = (function (venue) {
            return loadDetails(venue)
                .then(function (data) {
                    venue.info = data[2];
                    if (venue.info === undefined) {
                        venue.info = 'Whoops! Our data never showed up. Check out ' + venue.name + ' on Wikipedia for more!';
                    }
                    return createMarker(map, infowindow, venue);
                });
        })(venues[i]);
    }
    promises.push(request);
    return $.when.apply(null, promises);
}



var viewmodel = function () {
    var self = this;
    self.userSearch = ko.observable("");
    self.venues = ko.observableArray(venues);
    self.currentVenue = ko.observable(self.venues()[0]);
    self.setVenue = function (clickedVenue) {
        self.currentVenue(clickedVenue);
        google.maps.event.trigger(clickedVenue.marker, 'click');
    };

    self.filterVenues = ko.computed(function (venue) {
        var search = self.userSearch().toLowerCase();
        return ko.utils.arrayFilter(self.venues(), function (venue) {
            if (venue.name.toLowerCase().indexOf(search) !== -1) {
                venue.marker.setVisible(true);
                return true;
            } else {
                venue.marker.setVisible(false);
                return false;
            }
        });
    });
};


function initMap() {
    var map = new google.maps.Map(document.getElementById('map'), {
        center: { lat: 40.446855, lng: -80.0056666 },
        zoom: 14,
        mapTypeId: 'satellite'
    });

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

    initMarkers(map, infowindow)
        .done(function () {
            ko.applyBindings(new viewmodel());
            //console.log("map is ready");
        });

}

google.maps.event.addDomListener(window, 'load', initMap);
#map{
    width: 100%;
    height: 420px;
}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
 <script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
  <script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js'></script>
 <script type='text/javascript' src="https://maps.googleapis.com/maps/api/js">
 </script>
 
  
  
  <div id='overmap'>
      <div id='filter'>
        <div class='filter-bar'>
          <h2>Search Pittsburgh Sports Venues</h2>
          <input id='userFilter' data-bind="value:userSearch, valueUpdate: 'input'" placeholder="Type a venue name..." />
        </div>
        <ul id="list" data-bind="foreach: filterVenues">
          <a href='#'>
            <li>
              <h3 data-bind="text: name, click: $parent.setVenue"></h3>
            </li>
          </a>

        </ul>
      </div>
    </div>
    </div>
    <div id='map'></div>