🛠️ Google Maps Clone: Refactor
We’re done with the component. Let’s take a step back and clean up the code.
Getting search boxes
We used this line to get search boxes many times in the code:
const searchBoxes = [...searchPanel.querySelectorAll('.search-box')]
We can make this shorter (and also easier to read and understand) if we put it into a function.
For this function, we can use document
instead of searchPanel
. If we do this, we don’t need to pass in a variable (which makes it easier to use). This is only possible if there’s only one search panel in Google Maps clone.
const getSearchBoxes = _ => {
return [...document.querySelectorAll('.search-box')]
}
Initialise Autocomplete
First, you notice we used these three lines to initialise Google’s Autocomplete in two places:
// For the first two search boxes
searchBoxes.forEach(searchBox => {
const input = searchBox.querySelector('input')
const autocomplete = new google.maps.places.Autocomplete(input)
autocomplete.bindTo('bounds', map)
})
// For search boxes created with JavaScript
addSearchboxButton.addEventListener('click', event => {
// ...
const input = clone.querySelector('input')
const autocomplete = new google.maps.places.Autocomplete(input)
autocomplete.bindTo('bounds', map)
// ...
})
We can place these into a function called initGoogleAutocomplete
.
const initGoogleAutocomplete = searchBox => {
const input = searchBox.querySelector('input')
const autocomplete = new google.maps.places.Autocomplete(input)
autocomplete.bindTo('bounds', map)
}
We have to put initGoogleAutocomplete
inside initGoogleMaps
since it requires the map
element.
function initGoogleMaps () {
// ...
const map = new google.maps.Map(mapDiv, {
center: { lat: 1.3521, lng: 103.8198 },
zoom: 13
})
const initGoogleAutocomplete = searchBox => {
const input = searchBox.querySelector('input')
const autocomplete = new google.maps.places.Autocomplete(input)
autocomplete.bindTo('bounds', map)
}
}
Using initGoogleAutocomplete
:
// For the first two search boxes
searchBoxes.forEach(initGoogleAutocomplete)
// For search boxes created with JavaScript
addSearchboxButton.addEventListener('click', event => {
// ...
initGoogleAutocomplete(clone)
// ...
})
Getting the value of the search box
We had to use querySelector
to get the search box value:
searchPanel.addEventListener('submit', event => {
// ...
const request = {
origin: searchBoxes[0].querySelector('input').value.trim(),
destination: searchBoxes[searchBoxes.length - 1].querySelector('input').value.trim(),
travelMode: 'DRIVING'
}
// ...
})
We can make this easier by putting it into a function.
const getSearchBoxValue = searchBox => {
return searchBox.querySelector('input').value.trim()
}
Using it:
searchPanel.addEventListener('submit', event => {
const request = {
origin: getSearchBoxValue(searchBoxes[0]),
destination: getSearchBoxValue(searchBoxes.length - 1),
travelMode: 'DRIVING'
}
if (searchBoxes.length > 2) {
const waypoints = searchBoxes.slice(1, searchBoxes.length - 1)
.map(waypoint => {
return {
location: getSearchBoxValue(waypoint),
stopover: true
}
})
// ...
}
// ...
})
Drawing directions
There’s a chunk of code in the part we used to draw directions (especially with the error messages).
searchPanel.addEventListener('submit', event => {
getDirections(request)
.then(result => {
directionsRenderer.setDirections(result)
})
.catch(result => {
const errors = {
INVALID_REQUEST: 'Invalid request',
MAX_WAYPOINTS_EXCEEDED: 'Maximum of 8 waypoints allowed',
NOT_FOUND: 'At least one location cannot be geocoded',
OVER_QUERY_LIMIT: 'You sent too many requests in a short time. Slow down!',
UNKNOWN_ERROR: 'An error happened on the server. Please try again later',
ZERO_RESULTS: 'Cannot find route between origin and destination'
}
const message = errors[result.status]
const errorDiv = searchPanel.querySelector('.search-panel__error')
errorDiv.textContent = message
})
})
We can throw this into a function called drawDirections
. Make sure you put the function inside initGoogleMapsÂ
.
function initGoogleMaps () {
// ...
const drawDirections = request => {
getDirections(request)
.then(result => {
directionsRenderer.setDirections(result)
})
.catch(result => {
const errors = {
INVALID_REQUEST: 'Invalid request',
MAX_WAYPOINTS_EXCEEDED: 'Maximum of 8 waypoints allowed',
NOT_FOUND: 'At least one location cannot be geocoded',
OVER_QUERY_LIMIT: 'You sent too many requests in a short time. Slow down!',
UNKNOWN_ERROR: 'An error happened on the server. Please try again later',
ZERO_RESULTS: 'Cannot find route between origin and destination'
}
const message = errors[result.status]
const errorDiv = searchPanel.querySelector('.search-panel__error')
errorDiv.textContent = message
})
}
}
Using it:
searchPanel.addEventListener('submit', event => {
// ...
drawDirections(request)
})
We can pull out errorDiv
from drawDirections
to initGoogleMaps
. If we do this, we don’t have to search for errorDiv
again in the submit
event listener.
function initGoogleMaps () {
// ...
const errorDiv = searchPanel.querySelector('.search-panel__error')
const drawDirections = request => {
getDirections(request)
.then(/*...*/)
.catch(result => {
// ...
errorDiv.textContent = message
})
}
searchPanel.addEventListener('submit', event => {
// ...
errorDiv.textContent = ''
// ...
})
}
That’s it!