0

I've got a parent-child-relationship where the parent is called portfolio and the child called property object. All child objects have geolocation and address information.

I am implementing a lightning-map on the parent object and want to show all property objects by geolocation on the map.

The current problem is: When I do this dynamically with soql (by an apex class) no markers are shown. When I do this statically the markers are show.

That is my structure returning from the query (console.log):

[
    {
        "Name": "Hanseatic Trade Center Tower (Columbus Haus)",
        "Geolocation__Latitude__s": 53.542909,
        "Geolocation__Longitude__s": 9.985547,
        "Street__c": "Am Sandtorkai 38 – 41",
        "PostalCode__c": "20457",
        "City__c": "Hamburg",
        "Country__c": "Germany"
    }
]

This is the content from the map marker array:

[
    {
        "location": {
            "City": "Hamburg",
            "Country": "Germany",
            "PostalCode": "20457",
            "Street": "Am Sandtorkai 38 – 41"
        }
    }
]

My code looks like the following:

import { LightningElement, api, track, wire } from 'lwc';
import relatedObjects from '@salesforce/apex/LWCPortfolioHelper.relatedObjects';

export default class PortfolioMap extends LightningElement {
    @api recordId;
    @track mapMarkers = [];
    hasRendered = false;

    renderedCallback(){
        console.log('>>>PortfolioMap -- renderedCallback.');

        if(! this.hasRendered){
            this.hasRendered = true;
            this.handleLoad();
        }
    }

    handleLoad(){
        relatedObjects({recordId: this.recordId})
        .then(result => {
            result.forEach(record => {
                let marker = {
                    location: {
                        City: record.City__c,
                        Country: record.Country__c,
                        PostalCode: record.PostalCode__c,
                        Street: record.Street__c,
                    }

                };
                this.mapMarkers.push(marker);
            })

        })
        .catch(error => {
            console.error('>>>error: ' + error);
        })
    }
}

My template looks like the following (No marker on map):

<template>
    <lightning-map
        map-markers={mapMarkers}
        markers-title='All related objects to this portfolio'
        list-view='visible'
    ></lightning-map>
</template>

The result looks like the following: enter image description here

2 Answers 2

1

I would suspect it is a timing issue - you are rendering the template before the data is available, and so it doesn't re-render when data is loaded. I would suggest taking a look at the wire service in the docs - specifically using a function with a wire call, and then only rendering the template when the data is returned - e.g.

  @api recordId;
    @track record;
    @track error;

    @wire(getRecord, { recordId: '$recordId', fields: ['Account.Name'] })
    wiredAccount({ error, data }) {
        if (data) {
            this.record = data;
            this.error = undefined;
        } else if (error) {
            this.error = error;
            this.record = undefined;
        }
    }

And then in the template, use the if statement to handle the rendering - that way the component will take care of the component life cycle for you:

<template if:true={record}>
        <div class="slds-m-around_medium">
            <p>{name}</p>
        </div>
    </template>
2
  • Do you know the reason for this behaviour? Nov 27, 2019 at 15:18
  • As I understand it, @wire is async, and the tracked property is initialized as undefined (you can see it on the logs). Like push with arrays, changing the internal properties of an object doesn't force a re-render - only updates to primitive types do that for some reason. So when the wire returns a value, the template doesn't re-render. The if statement forces the template to render only when there is an actual value. Nov 27, 2019 at 15:28
0

I found the issue by myself. The distinctiveness is that you could not add object dynamically to an array.

the following DOES NOT work:

handleLoad(){
        console.log('>>>PortfolioMap -- handleLoad.');
        console.log('>>>this.recordId: ' + JSON.stringify(this.recordId, null, '\t'));

        searchRelatedObjectsBySoql({recordId: this.recordId})
        .then(result => {

            result.forEach(record => {
                let marker = {
                    location: {
                        Latitude: record.Geolocation__Latitude__s,
                        Longitude: record.Geolocation__Longitude__s
                    },
                    title: record.Name,
                    description: '',
                    icon: 'utility:salesforce1'
                };

                this.mapMarkers.push(marker);
            })

            this.mapMarkers = tempMarkers;
        })
        .catch(error => {
            console.error('>>>error: ' + error);
        })
    }

The following code works:

handleLoad(){
        searchRelatedObjectsBySoql({recordId: this.recordId})
        .then(result => {

            let tempMarkers = [];
            result.forEach(record => {
                let marker = {
                    location: {
                        Latitude: record.Geolocation__Latitude__s,
                        Longitude: record.Geolocation__Longitude__s
                    },
                    title: record.Name,
                    description: '',
                    icon: 'utility:salesforce1'
                };

                tempMarkers.push(marker);
            })

            this.mapMarkers = tempMarkers;
        })
        .catch(error => {
            console.error('>>>error: ' + error);
        })
    }

The differen lay in the following lines: 1) WORKS: tempMarkers.push(marker); this.mapMarkers = tempMarkers;

2) DON'T WORK: this.mapMarkers.push(marker);

2
  • Right - for some reason, just adding values to an array via push does NOT cause the component to re-render. But in example 2, you are changing the actual array object, which does then get seen as a change. Another way you'll see is to use spread operator to effectively re-initialize the array Nov 27, 2019 at 15:25
  • I'd still go with @BritishBoyinDC approach. It's simple and renders data section when data is loaded. Rest of the component sections continue to render in parallel. Instead of calling this.handleLoad in renderedCallback, call this.handleLoad in connectedCallback which starts apex call at component load. Nov 27, 2019 at 18:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .