Search code examples
typescriptgoogle-mapsvuejs3capacitor

Derived property value is not persistent when assigning it to an object value in Vue 3


I am trying to create an object to use in the Ionic Capacitor Google Maps API to configure the map at it's creation. Unfortunately, the value of the ref I use is not persistent and reverts to its default value. I have been trying to solve this issue for days with no success. Any help in showing how to make this work would be much appreciated. My code:

<script setup lang="ts">
import { computed, ref, onMounted } from "vue";
import { Geolocation } from "@capacitor/geolocation";

const currPos = ref<any | null>(null);
const lat = ref<number>(1);
const lng = ref<number>(2);

const getLocation = async () => {
  console.log("got getLocation event");
  currPos.value = await Geolocation.getCurrentPosition({
    enableHighAccuracy: true,
    timeout: 20000,
  });
  lat.value = currPos.value.coords.latitude;
  lng.value = currPos.value.coords.longitude;
};

const centerLat = computed(() => (lat.value ? lat.value : 3));

const mapConfig = {
  center: {
    // The initial position to be rendered by the map
    // lat: 32.78025, // this works
    // lng: -117.19335, // this works
    lat: centerLat.value as number, // not working, is 1
    lng: lng.value as number, // not working, is 2
  },
  zoom: 10,
};

const logLocData = () => {
  console.log("Latitude", lat.value); // shows correct number for lat
  console.log("Longitude", lng.value); // shows correct number for lng
  console.log("Center Latitude", centerLat.value); // shows correct number for centerLat
  console.log("MapCofig", mapConfig); // wrong, shows center and 1 for lat and 2 for lng
  console.log("MapCofig Latitude", mapConfig.center.lat); // wrong, shows 1 for lat
  console.log("MapCofig Longitude", mapConfig.center.lng); // wrong, shows 2 for lng
};

onMounted(() => {
  console.log("mounted");
  getLocation();
});
</script>

Console log:

mounted
got getLocation event
Latitude 32.7822162
Longitude -117.1852825
Center Latitude 32.7822162
MapCofig {center: {…}, zoom: 10}
MapCofig Latitude 1
MapCofig Longitude 2

Solution

  • mapConfig is not reactive.

    With minimal changes, this should work:

    mapConfig = computed(() => ({
      center: {
        lat: centerLat.value,
        lng: lng.value
      },
      zoom: 10,
    }))
    

    But I would personally go with this:

    <script setup lang="ts">
    import { computed, toRefs, onMounted, reactive } from "vue"
    
    type State = {
      lat: number
      lng: number
      centerLat: number
      mapConfig: {
        center: {
          lat: number
          lng: number
        }
        zoom: number
      }
      currPos: {
        coords: {
          latitude: number
          longitude: number
        }
      } | null
    }
    const state: State = reactive({
      currPos: null,
      lat: computed(() => state.currPos?.coords.latitude || 1),
      lng: computed(() => state.currPos?.coords.longitude || 2),
      centerLat: computed(() => state.lat || 3),
      mapConfig: computed(() => ({
        center: {
          lat: state.centerLat,
          lng: state.lng
        },
        zoom: 10
      }))
    })
    const getLocation = async () => {
      state.currPos = await Geolocation.getCurrentPosition({
        enableHighAccuracy: true,
        timeout: 20000
      })
    }
    const initMap() {
      // init map here...
    }
    onMounted(() => getLocation().then(initMap))
    
    // expose stuff to template, if you don't want the `state.` prefix:
    const { lat, lng, mapConfig } = toRefs(state)
    </script>
    

    I find it cleaner and easier to work with/follow (I'm guessing you'll be expanding it).