Customizing your location map

This article is work in progress. Any questions please get in touch. 

To allow you to create a completely customizable store locator we have used StimulusJS

StimulusJS allows you to only have to worry about the styling of your store locator, just add some data attributes and we will hook up the functionality to the app.

Stimulus Docs



Map wrapper

To create a map you need a wrapper around all map elements with a data-controller="map" e.g.
<div data-controller="map"> ... </div>

Values

You can change the values to control the map. A number of the values can be controlled in the theme settings.
    data-map-select-first-listing-on-results-value="false"
    data-map-bounds-padding-value='{ "left": 500, "right": 30, "top": 30, "bottom": 30 }'
    data-map-geolocate-on-load-value="{{map_settings.geolocate_on_load}}"
    data-map-filter-behaviour-value="{{map_settings.filter_behaviour}}"
    data-map-keep-search-in-view-value="{{map_settings.keep_search_in_view}}"
    data-map-max-distance-units-value="{{map_settings.distance_units}}"
    data-map-max-results-value="{{map_settings.max_results}}"
    data-map-max-distance-value="{{map_settings.max_distance}}"
    data-map-geocoder-options-value='{"placeholder": "{{language.map.search_placeholder}}"}'
    data-map-mapbox-options-value='{"cooperativeGestures": {{cooperative_gestures}}, "dragRotate": false}'
    data-map-animation-duration-value="1.2"

Using data-map-mapbox-options-value you can add custom propperties to your map. You can view the different properties on the mapbox

Map

All you need to display the map is a div with an attribute data-map-target="map" e.g.
<div data-map-target="map"></div>

Remember to put a height on this because it defaults to 0!

Access the map in Javascript

Access all Javascript elements which allows you to use the Mapbox API
First, add an ID to your map DIV in the landing template
  <div
    id="yourMapController"
    data-controller='map'

Then you can access it with

document.querySelector("#yourMapController").map
Inside you can access everything such as the map, geocoder, and geolocator.

e.g. to add fullscreen control you could add the following to the bottom of the landing template
<script>
const map = document.querySelector("#yourMapController").map.map
map.addControl(new mapboxgl.FullscreenControl({container: document.querySelector('body')}))
</script>
You may need wait for the DOM, you could use the following code:
<script>  
function docReady(fn) {
    // see if DOM is already available
    if (document.readyState === "complete" || document.readyState === "interactive") {
        // call on next available tick
        setTimeout(fn, 1);
    } else {
        document.addEventListener("DOMContentLoaded", fn);
    }
  }    
  docReady(function() {
      const map = document.querySelector("#yourMapController").map.map 
map.addControl(new mapboxgl.FullscreenControl({container: document.querySelector('body')})) }) </script>

With this, you can now access the Mapbox API and can add any of the Mapbox examples.

Search input

To display a geocoding search just add data-map-target="search" e.g.
<div data-map-target="search"></div>

Listings container

<div data-map-target="listingsContainer"></div>

This is where your listings will go once, they are received from the server.

Templates

Make sure to wrap the listing, marker, and popup templates in a {% raw %} liquid tag, so your liquid tags are not escaped and can be sent to the server. 

The listingsLoading and listingsEmpty templates don't need to be wrapped in a {% raw %} tag.

Listing 
<template data-map-target="listingTemplate">   ... </template>

When your listing template is rendered on the server it will be wrapped in a div with the class "listing" then inserted into your listings container element.


Marker 
(optional)
<template data-map-target="markerTemplate">   ... </template>
Popup  (optional)
<template data-map-target="popupTemplate">   ... </template><br>

Inside these 3 templates, you have access to the {{ location }} object, and {{ distance }}
You can display distance inside templates like this
{{ distance.in_miles }} or {{ distance.in_kilometers }}
If the user has not searched by location, there will be no distance so its best to do something like:
{% if distance != blank %}   <p>{{distance.in_miles}} miles away</p> {% endif %}
Listings loading contentThis is the content that will go inside the listingsContainer element while listings are loading
<template data-map-target="listingsLoadingContent">   ... </template>
Listings empty contentThis is the content that will go inside the listingsContainer element when no listings have been returned.
<template data-map-target="listingsEmptyContent">   ... </template><br>
These 5 template elements can be placed anywhere inside the map controller element.

Custom search marker
<template data-map-target="searchMarker">   <p>SEARCH MARKER CONTENT</p> </template><br>
If you dont have this, the default mapbox search marker will be used.

Result data

Once your listings are returned from the server, any element with an id of listingResultsFound will be populated with how many locations have been returned, and any with the id of "listingResultsTotal" will be populated with how many total locations there are.
e.g.
<p> Showing <span id="listingResultsFound">0</span> of <span id="listingResultsTotal">0</span> results </p><br>
You can add content inside these elements as placeholders, it will be replaced once their is a response from the server.

Filtering by tag

Just add these attributes to the tag
data-action="click->map#toggleTag" data-map-tag-id-param="{{tag.id}}"
you can change the data-action thing to anything e.g.
change->map#toggleTag<br>
For a checkbox, see stimulus docs

The class "active" will be added to active tag elements  

Filter by open now

use 
click->map#toggleOpenNow
e.g.
<div data-action="click->map#toggleOpenNow">Open now</div>

The class active will be added on this element when it is active

Search current map view

data-action="click->map#searchCurrentMapView"

Toggle on search as I move the map

data-action="click->map#toggleSearchAsIMoveTheMap"

When search as i move the map is enabled, the class "search-on-map-move" will be added to the container element

Clear tags

Just add
data-action="click->map#clearTags"

Geolocating

The map will have a geolocate button in the top right of the map,
you can hide this with 
.mapboxgl-ctrl-geolocate{     display: none !important;   }<br>
If you want to create your own geolocate element, just add the attribute
data-action="map#geolocate"
The class "loading" will be added to the element with this attribute when the map is trying to locate, then the class will be removed when the geolocating is done.

You can also add 
data-map-geolocate-on-load="true"
On the data-controller="map" element if you would like the map to locate the user once the map is loaded 

Clear search

data-action="map#clearSearchCoords"

Classes added to map

no-results results-loading geolocate-loading search-focused has-active-tags has-open-now-filter search-on-map-move<br>

Example map

<div data-controller='map'>

  <div class="simple-map-container">
    <div class="sidebar">
      <div class="sidebar-content">
        <h2>Find a cheeky store</h2>
        <div data-map-target="search" class="search"></div>
      </div>
      <p class="sidebar-results-text">Showing <span id="listingResultsFound">0</span> of <span id="listingResultsTotal">0</span> results</p>
      <div class="simple-listings" data-map-target='listingsContainer'></div>
    </div>

    <div class="map" data-map-target="map"></div>
  </div>

  {% comment %}Templates{% endcomment %}

  {% raw %}
    <template data-map-target="listingTemplate">
      <h4>{{location.name}}</h4>
      <p>{{location.fields.description}}</p>
      {% if distance %}
        <p>{{ distance.in_miles }} miles away</p>
      {% endif %}
    </template>
    
    <template data-map-target="markerTemplate">
      <img class="marker-image" src="data:image/svg+xml,%3Csvg width='36' height='36' viewBox='0 0 36 36' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M28.25 13.5044C28.25 19.3184 21.4985 28.8674 19.022 32.1734C18.5735 32.7734 17.6765 32.7734 17.228 32.1734C14.7515 28.8674 8 19.3184 8 13.5044C8 7.91239 12.533 3.37939 18.125 3.37939C23.717 3.37939 28.25 7.91239 28.25 13.5044Z' fill='%23111111'/%3E%3Ccircle cx='18' cy='13' r='4' fill='white'/%3E%3C/svg%3E" />
    </template>
    
    <template data-map-target="popupTemplate">
      <div class="popup-content">
        <h1>{{location.name}}</h1>
        <p>{{location.fields.description}}</p>
      </div>
    </template>
  {% endraw %}
  
  <template data-map-target="listingsLoadingContent">
    <div class="listings-loading-content">
      <p>Loading...</p>
    </div>
  </template>
  
  <template data-map-target="listingsEmptyContent">
    <div class="listings-empty-content">
      <p>We're sorry, we cant find any locations for you search</p>
      <p>Try changing the filters or the search location</p>
    </div>
  </template>

</div>
Was this page helpful? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Contact