Magento2 Checkout – How to Render Google Maps on Checkout in Magento 2

blockscheckoutjavascriptmagento2

I've extended magento 2 checkout page. Added shipping methods and custom fields to shipping method.Checkout page

Radio buttons are the list of locations. I need to add map for this locations. List of Locations were added to shipping method via extension attributes in plugin.

$extensionAttribute =  ($result->getExtensionAttributes())
        ? $result->getExtensionAttributes()
        : $this->extensionFactory->create();
$extensionAttribute->setStores($this->_getStores());

Than overrided magento/module-checkout/view/frontend/web/template/shipping.html and added code for rendering locations

<td class="col col-stores">
     <!-- ko if:  method.carrier_code + '_' + method.method_code == 'pickup_from_store_pickup_from_store' -->
         <div class="available-stores" data-bind="visible: $parent.isSelected() == 'pickup_from_store_pickup_from_store'">
             <div class="store-search-input">
                    <input type="text">
                    </div>
                    <div
                       class="store-list"
                       data-bind="foreach: method.extension_attributes.stores">
                       <div class="store-row">
                           <label>
                            <input type="radio"
                                   data-bind="
                                   value: $data.id,
                                   checked: $parentContext.$parent.selectedStore(),
                                   attr: {
                                       'id': 'store_id_' + $data.id,
                                       'name': 'offline_store'
                                   },
                                   click: $parentContext.$parent.selectStore.bind(this, $data.id)

                                            "
                                   class="radio"/>
                           <span data-bind="text: $data.name"></span>
                          </label>
                       </div>
             </div>
         <div id="map"></div>
     </div>

<!-- /ko -->

I need to insert google map in<div id="map"></div>
I've already included google maps library in separate block in it's phtml file.

<script src="https://maps.googleapis.com/maps/api/js?key=some_key&libraries=geometry"></script>

And in this template added also js that works with google api

<script type="text/x-magento-init">
{
    "*": {
        "Company_StoreSearch/js/store-search": {
            "stLocation": <?php echo $block->getStoreListJsonArray() ?>,
            "lpgLocation": <?php echo $storesByType['lpg'] ?>,
            "gardenLocation": <?php echo $storesByType['garden'] ?>,
            "extensiveToolSelectionLocation": <?php echo $storesByType['extensive_tool_selection'] ?>,
            "perishableLocation": <?php echo $storesByType['perishable'] ?>,
            "servicePointsLocation": <?php echo $storesByType['service_points'] ?>,
            "markerUrl": "<?php echo $block->getMarkerImageUrl() ?>",
            "locationMarkerUrl": "<?php echo $block->getLocationImageUrl() ?>",
            "searchUpdate": "<?php echo $block->getSearchUpdateUrl() ?>"
        }
    }
}

And added this block as child to 'checkout.root' in checkout_index_index.xml.
Than render this block in onepage.phtml like

<?php echo $block->getChildHtml('store_search_map') ?>

in the end of template

But i don't get how can i render google maps. Checkout page renders using knockout js and when i want to apply google maps api to my div in shipping.html – it doesn't exist yet.
I tried using $(document).ajaxStop(function () {…}), domReady! as parameter in my custom js. But nothing help.
Please help me. Thanks.

Best Answer

I was working on applying place autocomplete on checkout an here is a snippet with my code:

<script src="https://maps.googleapis.com/maps/api/js?key=lll&libraries=places&callback=notifySubscribers"
        async
        defer></script>

<script>
    var notifySubscribers = function () {
        require(['GoogleAddressLookup/model/apiLoadListener'], function (loadListener) {
            loadListener.isGoogleApiLoaded(true);
        })
    };
</script>

And my explanation. This phtml is rendered in after.body.start container and as you can see i basically do async load of google lib. The important part is that i pass callback function notifySubscribers. There as you can see i load my apiLoadListener class which is dedicated for holding information about the state of script in isGoogleApiLoaded ko observable. ApiLoadListener is by itself defined in separate file. Because isGoogleApiLoaded property is ko.observable then i can load apiListener module in any other module in which i can subscribe callback and execute appropriate logic. In your case it would be an initialization of google map on a particular element. I do it within onElementRender callback, here is an example snippet:

onElementRender: function (el) {
        if (!loadListener.isGoogleApiLoaded()) {
            loadListener.subscribe((isApiLoaded) => {
                if (isApiLoaded) {
                    this.initializer = new Initializer(el, Strategy, this.autocomplete_id);
                }
            });
        } else {
            this.initializer = new Initializer(el, Strategy, this.autocomplete_id);
        }
    },

As you can see, disregarding the unknown classes, i am basically listening for event dispatch from apiLoader to perform my logic. I hope that will help you out.