I wish to remove the duplication code brought by the anonymous function calls.
I am doing a very simple project where I use Google Maps API to show a map with two searchboxes. The user puts a start address and an end address in those boxes and I show markers in the map.
To achieve this I have two anonymous functions for the listeners, which are exactly equal except for one point - one uses the startSearchBox
and the other one the endSearchBox
.
This duplication of code is unnecessary, and so I tried to pass the searchboxes as a parameter to the anonymous function, however that didn't work.
I also considered creating the searchboxes as global variables, but that is a bad practice I wish to avoid.
How can I eliminate the duplication in this code?
function initSearchBoxes() {
// Create the search box and link it to the UI element.
let startInput = document.getElementById('start-input');
let startSearchBox = new google.maps.places.SearchBox(startInput);
let endInput = document.getElementById('end-input');
let endSearchBox = new google.maps.places.SearchBox(endInput);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
startSearchBox.setBounds(map.getBounds());
endSearchBox.setBounds(map.getBounds());
});
startSearchBox.addListener('places_changed', function() {
deleteAllMarkers();
let places = startSearchBox.getPlaces();
if (places.length == 0) {
return;
}
// For each place, get the icon, name and location.
let bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
// // Create a marker for each place.
let newMarker = createMarker(place.geometry.location, place.name, markerLabels.nextSymbol(), true);
markerLib.trackMarker(newMarker);
newMarker.setMap(map);
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
}
else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
endSearchBox.addListener('places_changed', function() {
deleteAllMarkers();
let places = endSearchBox.getPlaces();
if (places.length == 0) {
return;
}
// For each place, get the icon, name and location.
let bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
// // Create a marker for each place.
let newMarker = createMarker(place.geometry.location, place.name, markerLabels.nextSymbol(), true);
markerLib.trackMarker(newMarker);
newMarker.setMap(map);
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
}
else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
}
You can wrap your callback function in another "factory" function. The factory will take a parameter (the search box reference) and then it will return the actual handler:
function makeSearchHandler(searchBox) {
return function() {
deleteAllMarkers();
let places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// For each place, get the icon, name and location.
let bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
// // Create a marker for each place.
let newMarker = createMarker(place.geometry.location, place.name, markerLabels.nextSymbol(), true);
markerLib.trackMarker(newMarker);
newMarker.setMap(map);
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
}
else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
};
}
That function contains the code from your original, but instead of directly referring to either startSearchBox
or endSearchBox
, it uses the parameter passed to the factory. The returned function will therefore work like yours, but the code is only present once.
You can then use that function to create the callbacks:
startSearchBox.addListener('places_changed', makeSearchHandler(startSearchBox));
endSearchBox.addListener('places_changed', makeSearchHandler(endSearchBox));