Lightning Map – Fixing Refresh Issue in lightning:map

I'm trying the new lightning:map tag from the winter 19 and i'm having some issue.

I achieve to display a simple map filled with a list of objects. They are shown as markers on the map and also on a sidebar generate by the lightning:tag too (the "showFooter" parameter).

Problems :

1) When i'm changing the list, changes appear on the sidebar but markers on the map don't change (none appears or disappears).

2) When i have a list of 1 or less markers to show, the sidebar desapears. But if after i reset the list to 2 or more, the sidebar doesn't reappear.

I did a simplified code that show the problem :

component :

<aura:component>
    <!-- attributes -->
    <aura:attribute name="mapMarkers" type="Object"/>
    <aura:attribute name="markersTitle" type="String" />
    <aura:attribute name="showFooter" type="Boolean" />
    <aura:attribute name="center" type="Object" />
    <aura:attribute name="zoomLevel" type="Integer" />

    <!-- initialize map and "simulate" checkbox as clicked to fill the list -->
    <aura:handler name="init" value="{! this }" action="{! c.init }"/>
    <aura:handler name="init" value="{! this }" action="{! c.checkboxChanged }"/>

    <div>
        <lightning:input type="checkbox" name="checkbox" label="Change" aura:id="checkbox" onchange="{!c.checkboxChanged}"/>
        <lightning:button label="Clear" onclick="{!c.clear}"/>

        <lightning:map
                       mapMarkers="{! v.mapMarkers }"
                       center="{! v.center }"
                       zoomLevel="{! v.zoomLevel }"
                       markersTitle="{! v.markersTitle }"
                       showFooter="{ !v.showFooter }" >
        </lightning:map>
    </div>
</aura:component>

Controller :

({
    init : function(component, event, helper)
    {
        //initialize the map
        component.set('v.center', {
            location: {
                City: 'Fréjus'
            }
        });

        component.set('v.zoomLevel', 9);
        component.set('v.markersTitle', 'Some people');
        component.set('v.showFooter', true);
    },
    checkboxChanged : function(component, event, helper)
    {
        //swap between two lists of markers
        var check = component.find('checkbox').get('v.checked')

        if(check == true) //list 1
        {
            component.set("v.mapMarkers", [
            {
                location: {
                    City: 'Cap-d\'Ail',
                    Country: 'France',
                },

                icon: 'custom:custom26',
                title: 'Cap-d\'Ail'
            },
            {
                location: {
                    City: 'Beaulieu-sur-Mer',
                    Country: 'France',
                },

                icon: 'custom:custom96',
                title: 'Beaulieu-sur-Mer'
            }]);
        }
        else //list 2
        {
            component.set("v.mapMarkers", [{
                location: {
                    City: 'Fréjus',
                    Country: 'France',
                },

                icon: 'custom:custom88',
                title: 'Fréjus'
            },
            {
                location: {
                    City: 'Sainte-Maxime',
                    Country: 'France',
                },

                icon: 'custom:custom92',
                title: 'Sainte-Maxime'
            },
            {
                location: {
                    City: 'Saint-Tropez',
                    Country: 'France',
                },

                icon: 'custom:custom26',
                title: 'Saint-Tropez'
            }
            ]);
        }
    },
    clear : function(component, event, helper)
    {
        //clear the current list showed
        component.set("v.mapMarkers", new Array());
    }
})

I know it's early to have much information about it, but this tag is really interesting.

Thanks for any help !

Best Answer

I tried out the lightning:map too and I had the same issue: it wouldn't re-render no matter what I did. It's an issue with the map itself, keep in mind it's still beta, but I believe they will solve the issue pretty soon. I think that by now you could have found a workaround for this, but I'm going to post my solution based on your code, in case someone else runs into the same issue as we did. I hope this saves some time to someone else.

Remember: This is a workaround, things are not intended to be done like this for other Lightning Components. They usually refresh the view based on events that the platform provides.

In my case, I had a record with address information and right next to it the address was displayed in the lightning:map, so if the address was updated, I wanted the map to update accordingly, without any extra steps for the user.

1. For the map to update

MyMap.cmp

<aura:component implements="flexipage:availableForAllPageTypes">
    <!-- attributes -->
    <aura:attribute name="mapMarkers" type="Object" default="" />
    <aura:attribute name="markersTitle" type="String" />
    <aura:attribute name="showFooter" type="Boolean" />
    <aura:attribute name="center" type="Object" />
    <aura:attribute name="zoomLevel" type="Integer" />
    <aura:attribute name="list1" type="Object[]" default="[]" />
    <aura:attribute name="list2" type="Object[]" default="[]" />

    <!-- initialize map and "simulate" checkbox as clicked to fill the list -->
    <aura:handler name="init" value="{! this }" action="{! c.init }"/>
    <!-- <aura:handler name="init" value="{! this }" action="{! c.checkboxChanged }"/> -->

    <div>
        <lightning:input type="checkbox" name="checkbox" label="Change" aura:id="checkbox" onchange="{!c.checkboxChanged}"/>
        <lightning:button label="Clear" onclick="{!c.clear}"/>

        <div aura:id="map-container"> <!-- I needed to add this too -->
            <lightning:map
                mapMarkers="{! v.mapMarkers }"
                center="{! v.center }"
                zoomLevel="{! v.zoomLevel }"
                markersTitle="{! v.markersTitle }"
                showFooter="{ !v.showFooter }" >
            </lightning:map>
        </div>
    </div>
</aura:component>

Line 3 Initialize the markers either here or in the controller, as mentioned by Jihad AZAMI HASSANI. This might not be an issue to you, but it can be for others because when the markers come from async data (a query, the Lightning Data Service, etc). You may get an exception because it tries to render and that variable doesn't have a value. Initializing with just default="" will do.

Lines 8 and 9 I added those variables there to use the lists in different methods in the controller.

Line 13 You won't need to trigger that manually from the markup, I replicated that in the controller.

Line 19 I needed to wrap the lightning:map inside a div, I used that in the helper.


MyMapController.js

init : function(component, event, helper)
{
    //initialize the map
    component.set('v.center', {
        location: {
            City: 'Fréjus'
        }
    });

    component.set('v.zoomLevel', 9);
    component.set('v.markersTitle', 'Some people');
    component.set('v.showFooter', true);

    // I moved the lists here to simplify the code that updates the map
    component.set('v.list1', [
        {
            location: {
                City: 'Cap-d\'Ail',
                Country: 'France',
            },

            icon: 'custom:custom26',
            title: 'Cap-d\'Ail'
        },
        {
            location: {
                City: 'Beaulieu-sur-Mer',
                Country: 'France',
            },

            icon: 'custom:custom96',
            title: 'Beaulieu-sur-Mer'
        }
    ]);

    component.set('v.list2', [
        {
            location: {
                City: 'Fréjus',
                Country: 'France',
            },

            icon: 'custom:custom88',
            title: 'Fréjus'
        },
        {
            location: {
                City: 'Sainte-Maxime',
                Country: 'France',
            },

            icon: 'custom:custom92',
            title: 'Sainte-Maxime'
        },
        {
            location: {
                City: 'Saint-Tropez',
                Country: 'France',
            },

            icon: 'custom:custom26',
            title: 'Saint-Tropez'
        }
    ]);

    helper.updateMarkers(component, component.get('v.list2'));
},
checkboxChanged : function(component, event, helper)
{
    //swap between two lists of markers
    var check = component.find('checkbox').get('v.checked')

    if(check == true) //list 1
    {
        helper.updateMarkers(component, component.get('v.list1'));
    }
    else //list 2
    {
        helper.updateMarkers(component, component.get('v.list2'));
    }
},
clear : function(component, event, helper)
{
    //clear the current list showed
    helper.updateMarkers(component, []);
}

Line 15 I moved the lists there to simplify the code that updates the displayed list, but you can move them back if you would like to.

Line 67 That line does what Line 13 did in MyMap.cmp.

Lines 67, 76, 80 and 86 As you can see, I encapsulated the repeated code in MyMapHelper.js following the expected design pattern.


MyMapHelper.js

updateMarkers: function(component, markers) {
    let container = component.find('map-container');
    $A.createComponent(
        'lightning:map',
        {
            mapMarkers: markers,
            zoomLevel: component.get('v.zoomLevel')
        },
        (newComponent) => {
            container.set('v.body', [newComponent]);
        }
    );
}

updateMarkers Basically, what I'm doing is overriding the content of the 'map-container' div element for a brand new map, so I get a new component that gets initialized. As a side note, every time you change the data, the map's animation will play again. In your case, you should add the other properties that you were passing to the map in the markup, like center, markersTitle, etc. I think you wouldn't even need to have the original lightning:map tag in the markup file.

Note: As a different pattern, you could create a custom renderer, as MyMapRenderer.js, but it's a more complex approach. See here.



2. When i have a list of 1 or less markers to show, the sidebar desapears.

I think that's a default behavior and I also think it can't be changed, like manually forcing the sidebar to show. I've been looking at the available information for in the Lightning Components Library and there aren't any properties that seem to have anything to do with that. They may include this in the future.

2. But if after i reset the list to 2 or more, the sidebar doesn't reappear.

This probably has to do with the re-rendering issue we addressed in Question 1, try again after using my code 😉

GOOD LUCK!

Related Topic