Mapping out your data effectively requires following the correct steps to ensure accuracy and clarity

My goal is to display a map using Mapbox only once the data is ready.

The Vuex store code I am working with is as follows:

/store/index.js

import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";

import { backendCaller } from "src/core/speakers/backend";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    // Activity
    activity: [],
    geoIps: [],
  },

  mutations: {
    // Activity
    setActivity: (state, value) => {
      state.activity = value;
    },
    setGeoIp: (state, value) => {
      state.geoIps.push(value);
    },
  },

  actions: {
    // Activity
    async FETCH_ACTIVITY({ commit, state }, force = false) {
      if (!state.activity.length || force) {
        await backendCaller.get("activity").then((response) => {
          commit("setActivity", response.data.data);
        });
      }
    },
    async FETCH_GEO_IPS({ commit, getters }) {
      const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
        return await Vue.prototype.$axios
          .get(
            `http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
          )
          .then((response) => {
            return response.data;
          });
      });

      geoIpsPromises.map((geoIp) => {
        return geoIp.then((result) => {
          commit("setGeoIp", result);
        });
      });
    },
  },

  getters: {
    activityIps: (state) => {
      return _.uniq(state.activity.map((activityRow) => activityRow.ip));
    },
  },

  strict: process.env.DEV,
});

In my App.vue, I fetch all API requests using an asynchronous created method.

App.vue:

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>

export default {
  name: "App",

  async created() {
    await this.$store.dispatch("FETCH_ACTIVITY");
    await this.$store.dispatch("FETCH_GEO_IPS");
  },
};
</script>

Within my Dashboard component, I have implemented conditional rendering to display the maps component only when geoIps.length > 0

Dashboard.vue:

<template>
  <div v-if="geoIps.length > 0">
    <maps-geo-ips-card />
  </div>
</template>

<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";

export default {
  name: "dashboard",

  components: {
    mapsGeoIpsCard,
  },

  computed: {
    activity() {
      return this.$store.state.activity;
    },
    activityIps() {
      return this.$store.getters.activityIps;
    },
    geoIps() {
      return this.$store.state.geoIps;
    },
};
</script>

Subsequently, I proceed to load the Maps component.

<template>
  <q-card class="bg-primary APP__card APP__card-highlight">
    <q-card-section class="no-padding no-margin">
      <div id="map"></div>
    </q-card-section>
  </q-card>
</template>

<script>
import "mapbox-gl/dist/mapbox-gl.css";
import mapboxgl from "mapbox-gl/dist/mapbox-gl";

export default {
  name: "maps-geo-ips-card",

  computed: {
    geoIps() {
      return this.$store.state.geoIps;
    },
  },

  created() {
    mapboxgl.accessToken = process.env.MAPBOX_API_KEY;
  },

  mounted() {
    const mapbox = new mapboxgl.Map({
      container: "map",
      center: [0, 15],
      zoom: 1,
    });

    this.geoIps.map((geoIp) =>
      new mapboxgl.Marker()
        .setLngLat([geoIp.longitude, geoIp.latitude])
        .addTo(mapbox)
    );
  },
};
</script>

<style>
#map {
  height: 500px;
  width: 100%;
  border-radius: 25px;
  overflow: hidden;
}
</style>

The issue arises when the function resolves the first IP address, leading to the map displaying only one address instead of multiple addresses like so:

https://i.sstatic.net/0N0SZ.png

I am seeking advice on the best approach to render the map only after the completion of my FETCH_GEO_IPS function.

Thank you in advance!

Answer №1

The solution can be found in the following piece of code:

      geoIpsPromises.map((geoIp) => {
        return geoIp.then((result) => {
          commit("setGeoIp", result);
        });
      });

The map function iterates over each element of the array and commits each IP individually. As a result, when the first IP is committed, the condition

v-if="geoIps.length > 0"
evaluates to true.

To address this issue, a flag can be set only when the IPs are successfully initialized. Here's a proposed workaround:

import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";

import { backendCaller } from "src/core/speakers/backend";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    // Activity
    activity: [],
    geoIps: [],
    isReady: false
  },

  mutations: {
    // Activity
    setActivity: (state, value) => {
      state.activity = value;
    },
    setGeoIp: (state, value) => {
      state.geoIps.push(value);
    },
    setIsReady: (state, value) => {
        state.isReady = value;
    }
  },

  actions: {
    // Activity
    async FETCH_ACTIVITY({ commit, state }, force = false) {
      if (!state.activity.length || force) {
        await backendCaller.get("activity").then((response) => {
          commit("setActivity", response.data.data);
        });
      }
    },
    async FETCH_GEO_IPS({ commit, getters }) {
        let tofetch = getters.activityIps.length; // get the number of fetch to do
      const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
        return await Vue.prototype.$axios
          .get(
            `http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
          )
          .then((response) => {
            return response.data;
          });
      });

      geoIpsPromises.map((geoIp) => {
        return geoIp.then((result) => {
          commit("setGeoIp", result);
          toFetch -= 1; // decrease after each commit
          if (toFetch === 0) {
            commit("setIsReady", true); // all commits completed
          }
        });
      });
    },
  },

  getters: {
    activityIps: (state) => {
      return _.uniq(state.activity.map((activityRow) => activityRow.ip));
    },
  },

  strict: process.env.DEV,
});

In your view component:

<template>
  <div v-if="isReady">
    <maps-geo-ips-card />
  </div>
</template>

<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";

export default {
  name: "dashboard",

  components: {
    mapsGeoIpsCard,
  },

  computed: {
    activity() {
      return this.$store.state.activity;
    },
    activityIps() {
      return this.$store.getters.activityIps;
    },
    isReady() {
      return this.$store.state.isReady;
    },
};
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Steps to replace the content of an HTML file (such as modifying images) by clicking on an element in a separate HTML file

Currently, I am in the midst of a project and wondering if it is possible to dynamically modify the content of an HTML file, such as images and text, using JavaScript. My goal is to achieve this without relying on any frameworks, simply by clicking on el ...

Creating a Vue Canvas with Endless Grid Dots and a Dynamic Panning Feature

I'm currently focused on integrating a panning system into the canvas of my Vue application. Although I have successfully implemented the panning system, I am encountering difficulties in efficiently rendering an infinite grid of dots. Below is the c ...

Storing dynamic content on a server and retrieving it for future use

I'm working on a webpage that allows users to create elements dynamically and I want to save those elements to the server. When someone else visits the page, I want them to see those saved elements as well. I'm not too familiar with web programm ...

Mastering the art of adding content slot to tooltips in Buefy

I am attempting to utilize Buefy's tooltip feature with a content slot. Instead of using the label prop to define the tooltip text, I want to use HTML tags for text formatting. Upon inspecting the source code of the tooltip component on GitHub, it ap ...

Refreshing the v-model in a child component

Within my parent component, the code structure is similar to this: <template> <ProductCounter v-model="formData.productCount" label="product count" /> </template> <script setup> const initialFormData = { ...

Arranging and Filtering an Object Array Based on their Attributes

Here is an example of a JSON array called items: var items = [ { Id: "c1", config:{ url:"/c1", content: "c1 content", parentId: "p1", parentUrl: "/p1", parentContent: "p1 content", } }, { Id: "c2", ...

A guide on validating dates in Angular Ionic5 with the help of TypeScript

I have tried multiple solutions, but none seem to work when validating the current date with the date entered by the user. The date is passed from the user into the function parameters, but how do I perform validation? How can I validate the date? isToday( ...

Change the data-theme using jQuery Mobile once the page has finished loading

I am facing an issue with my buttons in a control group that represent on/off functionality. Every time I click on one of the buttons to switch their themes, the theme reverts back once I move the mouse away from the button. How can I make sure that the ...

Navigating AngularJS with multiple external files and folders

Recently dove into Angular and hit a roadblock with routing. I followed the setup instructions, but for some reason it's not functioning as expected. index.html: <!DOCTYPE html> <html lang="en> <head> <meta charset="utf-8> ...

What is the best way to change an array of strings into a single string in JavaScript?

I am working with a JavaScript array that contains strings arranged in a specific format: arrayOfString = ['a', 'b,c', 'd,e', 'f']; My goal is to transform this array into a new format like so: myString = ["a", "b ...

Tsyringe - Utilizing Dependency Injection with Multiple Constructors

Hey there, how's everyone doing today? I'm venturing into something new and different, stepping slightly away from the usual concept but aiming to accomplish my goal in a more refined manner. Currently, I am utilizing a repository pattern and l ...

"Transferring a variable from the parent Layout component to its children components in Next

I am trying to figure out how to effectively pass the variable 'country' from the Layout component to its children without using state management. Basically, I want to drill it down. import { useState, useEffect } from 'react' import La ...

The JavaScript-rendered HTML button is unresponsive

I have a JavaScript function that controls the display of a popup window based on its visibility. The function used to work perfectly, with the close button effectively hiding the window when clicked. However, after making some changes to the code, the clo ...

JavaScript code does not run when a page is being included

On my AngularJS-based page, I have included some additional HTML pages like this: <div data-ng-include src="includehtml"></div> Here is the JavaScript code: $scope.selectImage = function(id) {$scope.includehtml = id;} (I pass the HTML file ...

What is the most effective way to alphabetically organize a Javascript array?

Is there a more professional way to sort people alphabetically by last name in an array? Here is the array I'm working with: const people = [ 'Bernhard, Sandra', 'Bethea, Erin', 'Becker, Carl', 'Bentsen, Lloyd' ...

Different approaches to transforming jQuery code into functional AngularJS code

I am a beginner in AngularJS and I'm looking to implement functionality for a login page similar to the one you see when you click the 'Forgot Password' link: Would it be more appropriate to use a directive instead of a controller for this ...

While attempting to import modules in Visual Studio Code, an error message appears stating "Unexpected token {"

Greetings! I am currently using Visual Code to run my project and would like to share my code with you. In the file external.js: export let keyValue=1000; In the file script.js: import {keyValue} from './external.js'; console.log(keyValue); ...

JavaScript equivalent code to C#'s File.ReadLines(filepath) would be reading a file line

Currently in my coding project using C#, I have incorporated the .NET package File.ReadLines(). Is there a way to replicate this functionality in JavaScript? var csvArray = File.ReadLines(filePath).Select(x => x.Split(',')).ToArray(); I am a ...

Guide on changing image source using a function

I am looking to dynamically set the img src="" attribute using a JavaScript function that changes the image based on a variable and checks its values: Here is the JavaScript code in the file: function myFunctionstatus(){ var ledactual = document.getE ...

What is the reason behind installing both Typescript and Javascript in Next.js?

After executing the command npx create-next-app --typescript --example with-tailwindcss my_project, my project ends up having this appearance: https://i.stack.imgur.com/yXEFK.png Is there a way to set up Next.js with Typescript and Tailwind CSS without i ...