3

In LWC, on the component load, I am fetching the latitude and longitude of the logged in user and trying to display it on the component. I have created two attributes named lat and lng and marked it with @track decorator so that whenever the value of these attributes is changed, the component will rerender automatically. I am setting the value of these attributes in connectedCallback, but the updated value is not getting reflected on the component. Below is the html and js files.

<template>
    <div class="slds-box">
        <div class="slds-text-heading--large">Weather Report - {lat} {lng}</div>
    </div>
</template>
import { LightningElement, track } from 'lwc';

export default class WeatherAppContainer extends LightningElement {
    @track lat;
    @track lng;
    connectedCallback() {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(showPosition);
        }

        function showPosition(position) {
            console.log("position: ",position);
            console.log("position coords: ",position.coords);
            console.log("position coords lat: ",position.coords.latitude);
            this.lat = position.coords.latitude; 
            this.lng = position.coords.longitude; 
            console.log("lat: ",this.lat);
            console.log("lng: ",this.lng);
        }
    }

}

The console "position coords lat" prints the value correctly, but the next console statment does not get printed. I am guessing that we cannot use this keyword in the connectedCallback. Any pointers how to solve this? Thanks.

4

this is quite funny in javascript, in connectedCallback this refers to component, but in showPosition as its async method, this is undefined, and thus you cannot set lat and long

Usually, for this scenario, we pass the current component reference this to showPosition method like one here. But as per Mozilla document , you cant pass custom parameter to getCurrentPositionCallback

You have to use fatArrow function for getCurrentPositionCallback.

Thus your JS code will be:

import { LightningElement, track, api } from 'lwc';

export default class App extends LightningElement {
    @track lat;
    @track lng;
    connectedCallback() {
        console.log('Connected callback called'+ navigator.geolocation);
        console.log(JSON.stringify(navigator.geolocation));
        let myComponent = this;
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition((position)=>{
            console.log("position: ",position);
            console.log("position coords: ",position.coords);
            console.log("position coords lat: ",position.coords.latitude);
            myComponent.lat = position.coords.latitude; 
            myComponent.lng = position.coords.longitude; 
            console.log("lat: ",myComponent.lat);
            console.log("lng: ",myComponent.lng);
            });
        }


    }


}

Playground Link : https://developer.salesforce.com/docs/component-library/tools/playground/htbBevIZr/2/edit

  • Thanks Pranay. Do you see any problem with below code: – Ravi Dutt Sharma Jul 15 at 10:44
  • connectedCallback() { let self = this; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(showPosition); } function showPosition(position) { self.lat = position.coords.latitude; self.lng = position.coords.longitude; } } – Ravi Dutt Sharma Jul 15 at 10:45
  • 1
    @RaviDuttSharma that will do. Fat Arrow functions are the elegant way to write inner function. – Pranay Jaiswal Jul 15 at 11:27
3

The problem lies with how this behaves in JavaScript. I strongly recommend reading https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

As to fixing your code, this one line change does the trick:

navigator.geolocation.getCurrentPosition(showPosition.bind(this));

You can read about bindat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

  • 1
    +1, There is always an elegant way in javascript. – Pranay Jaiswal Jul 15 at 13:57
0

this will be window object as functions in navigator returns promises - which makes it async methods/functions. I already answered this here: LWC property is not reactive

However I am putting it here again:

This pitfall is quite common for salesforce developers working on Javascript. Set-Interval actually changes the context of this to window as functions like setInterval, setTimeout etc are async functions and hence these functions run after the main thread has finished processing. This can be addressed by any of 2 options:

  1. Lexical Scoping - old fashioned (pls do a google search for understanding)
  2. Arrow functions - Modern javascript. (PFB)

Just change the callback function from:

setInterval(function(){}, 500);

To

setInterval(() => {}, 500);

For simple understanding, Arrow functions don't have their own scope because of which it takes the scope of parent object.

There are many other advantages of arrow functions. Further read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.