Challenge:

As a user, while browsing locations or businesses on the SXA Map, I want to see the Google average rating and total reviews in the info window so I can decide on a better location or business.

Solution:

To set up the SXA Map on the page, please check the first post SXA Map component.

A quick look at the following output of the implementation discussed in this post so we get more clarity.

SXA Map Google Average Rating And Review Count – Quick look

Add a new POI Variant

Let’s add a new POI variant where we will add rating and review implementation.

In the Content Editor, traverse to the following Default item in your tenant site.

/sitecore/content/<tenant>/<site>/Presentation/Rendering Variants/POI/Default

Duplicate this and rename it to “Marker with Rating and Review”. Now we have the following item.

/sitecore/content/<tenant>/<site>/Presentation/Rendering Variants/POI/Marker with Rating and Review

Right-click this new item and add a Scriban item. Let’s name it as Rating and Review.

Copy the following Scriban code to the Template field of the “Rating and Review” Scriban item.

{{ divid = i_item.id | string.remove ‘{‘ | string.remove ‘}’ }}
<div id='{{divid}}-rating-and-review-main’ class=’rating-and-review-main’>
<div class=’title’>
{{i_item.title}}
</div>
<div class=’description’>
{{i_item.description}}
</div>
<div class=’rating-and-review d-none’ id='{{divid}}-rating-and-review’>
Average Rating #average_rating# Review count #review_count#
</div>
<div class=’no-rating-and-review’ id='{{divid}}-no-rating-and-review’>
No reviews
</div>
</div>

Feel free to move the Texts – “Average Rating”, “Review count”, and “No reviews”, to the Sitecore dictionary items later.

Add the new POI variant to the new POI Type

Traverse to the following Simple POI item.

/sitecore/content/<tenant>/<site>/Presentation/POI Types/Simple POI

Duplicate this and rename it “Simple POI with Rating and Review”. Now we have the following item.

/sitecore/content/<tenant>/<site>/Presentation/POI Types/Simple POI with Rating and Review

Update the field “Default variant” with “Marker with Rating and Review”, i.e., the one we created in the previous step.

Mapping new POI variant to Map

We have two options to map the new variant, i.e., “Marker with Rating and Review” to the SXA Map on the page. Follow any one of the below.

Update the Type field to “Simple POI with Rating and Review” on every POI item those are referenced in the Map data source. POI items reside under /sitecore/content/<tenant>/<site>/Data/POIs.
Update the “POI type to rendering variant mapping” field as below, i.e., “Simple POI” POI type maps to “Marker with Rating and Review”. This option is useful when we want to reuse the POI items and don’t want to change the POI Type in POI items. Instead, override the variant for POI Type in the separate Map data source itself.

Update the “POI type to rendering variant mapping” field to override the variant for the POI type set on the POI items.

Bind Google average rating and total reviews Data

custom-google-maps-connector.js

Download the custom-google-maps-connector.js and custom-location-rating-and-review.js, and upload both the files to the site theme script folder /sitecore/media library/Themes/{tenant}/{site}/{theme}/Scripts or copy to your <RootThemeFolder> <SiteTheme>scripts folder.

Difference Between Original and Customized Google Maps Connector.js

custom-google-maps-connector.js inherits the logic from /sitecore/media library/Base Themes/Google Maps JS Connector/Scripts/google-maps-connector and overrides to trigger an event upon InfoWindow is opened, and this event is listened to in custom-location-rating-and-review.js code. custom-google-maps-connector.js also provides a function to access the maps.

custom-location-rating-and-review.js

XA.component.customLocationRatingAndReview = (function ($, document) {
/* eslint-disable */
“use strict”;
var api = {},
scriptsLoaded = false;

api.init = function() {
if ($(“body”).hasClass(“on-page-editor”)) {
return;
}
if(!scriptsLoaded && document.querySelector(‘.component.map’))
{
scriptsLoaded = true;
api.listenInfowindowOpenEvent();
}
};

api.setRatingAndReviewBySearchText = function(ratingAndReviewDivId, noRatingAndReviewDivId, searchText) {
var ratingAndReviewDiv = $(`#${ratingAndReviewDivId}`);
var noRatingAndReviewDiv = $(`#${noRatingAndReviewDivId}`);
if(!(ratingAndReviewDiv.html().indexOf(‘#average_rating#’) > -1) && !(ratingAndReviewDiv.html().indexOf(‘#review_count#’) > -1))
return;

var request = {query: searchText, fields: [‘place_id’]};
var sxamap = XA.connector.mapsConnector.getMaps()[$(‘.map-canvas’).attr(‘id’)];
var service = new google.maps.places.PlacesService(sxamap);
service.findPlaceFromQuery(request, requestPlaceDetails(ratingAndReviewDiv, noRatingAndReviewDiv));
}

function requestPlaceDetails(ratingAndReviewDiv, noRatingAndReviewDiv) {
return function (results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
var request = {
placeId: results[0].place_id,
fields: [‘rating’, ‘user_ratings_total’]
};
var sxamap = XA.connector.mapsConnector.getMaps()[$(‘.map-canvas’).attr(‘id’)];
var service = new google.maps.places.PlacesService(sxamap);
service.getDetails(request, getRatingAndReviewByPlaceID(ratingAndReviewDiv, noRatingAndReviewDiv));
}
};
}

function getRatingAndReviewByPlaceID(ratingAndReviewDiv, noRatingAndReviewDiv) {
return function (place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
if(noRatingAndReviewDiv.length > 0 && ratingAndReviewDiv.length > 0)
{
if (place.rating != undefined && place.user_ratings_total != undefined) {
ratingAndReviewDiv.toggleClass(‘ratings-and-reviews’);
ratingAndReviewDiv.removeClass(‘d-none’);
ratingAndReviewDiv.html(ratingAndReviewDiv.html().replace(‘#average_rating#’, `${place.rating.toFixed(1)} <i data-star=”${place.rating.toFixed(1)}”></i>`).replace(‘#review_count#’, place.user_ratings_total));
noRatingAndReviewDiv.remove();
}
}
}
};
}

api.listenInfowindowOpenEvent = function() {
$(document).on(‘infowindowOpen’, function(event, infoWindow) {
var openedInfoWindowsRatingReviewMainDiv = $(‘.rating-and-review-main’);
if(openedInfoWindowsRatingReviewMainDiv.length > 0) {
openedInfoWindowsRatingReviewMainDiv.each(function() {
var ratingReviewMainDivId = $(this).attr(“id”);
var poiId = ratingReviewMainDivId.replace(‘-rating-and-review-main’,”);
var ratingReviewDivId = `${poiId}-rating-and-review`;
var noRatingReviewDivId = `${poiId}-no-rating-and-review`;
var locationTitleText = $(`#${ratingReviewMainDivId} .title`).text();
api.setRatingAndReviewBySearchText(ratingReviewDivId,noRatingReviewDivId,locationTitleText);
});
}
});
}

return api;

}(jQuery, document));

XA.register(“CustomLocationRatingAndReview”, XA.component.customLocationRatingAndReview);

In POI items, please provide the proper Business location name and address to get accurate Rating and Review details. The “Title” field can comprise the value in the format “Business or Location name, address, city, country”. In custom-location-rating-and-review.js, when the “infowindowOpen” event has listened, we retrieve the Title value for the clicked marker and use this Title value to fetch the place_id, and after that, fetch the rating and review.

We can introduce separate fields for each of the info and make necessary code changes in Scriban and JS code to concatenate all the field values to form the desired complete address and pass as 3rd parameter to our custom function setRatingAndReviewBySearchText() in custom-location-rating-and-review.js.

Here, requesting place_id is free of charge since we call getDetails() and have specified only the place_id field for response. (Reference)

Nearby Search and Text Search return all the available data fields for the selected place (a subset of the supported fields), and you will be billed accordingly. There is no way to constrain Nearby Search or Text Search to only return specific fields. To keep from requesting (and paying for) data that you don’t need, use a Find Place request instead. Hence, we have used findPlaceFromQuery(). (Reference)

component-map-location-rating.scss

Download the component-map-location-rating.scss and copy it to your <RootThemeFolder> <SiteTheme>sass folder. Run the following commands in PowerShell from the Site Theme folder, or build with your custom command, if any.

gulp buildAll

sxa upload All

Demo

Final demo showing the items and page.

Final Demo – Google average rating and total reviews

Google Map Javascript Documentation references:

Find Place requests

Place Details Requests

Hope this helps, and please do check out other SXA Map component posts for more enhancements around Map.

Happy Sitecore Learning