Home > front end >  Vue data fetched from API on onMounted() not displayed in DOM. Appears when I modify the template in
Vue data fetched from API on onMounted() not displayed in DOM. Appears when I modify the template in

Time:01-26

First of all, thank you for those who will be looking at this. I am a beginner with Vue so I might be missing something obvious here but after several days stuck, here I am

On a SFC with , I have a onMounted fetching data from an API (Spring Boot). The API call works, I can see it under Netwwork in Chrome, the Response contains the data I need.

My AppLeague.vue file

<template lang="">
    <div >
        <div >
            <h3>Leagues</h3>
        </div>
        <div >
            <ul >
                <li
                    v-for="(leagueItem, index) in leagues"
                    :key="index"
                    
                >
                    {{ index   1 }}.{{ leagueItem.league_name }}
                    , Status:
                    {{ leagueItem.league_status }}, Code: {{ leagueItem.league_code }}
                </li>
            </ul>
        </div>
    </div>
</template>

<script setup lang="ts">
import { onMounted } from "vue";
import customAxios from "@/lib/customAxios";

interface League {
    league_id: number,
    league_name: string;
    league_code: string;
    league_status: string;
}

let leagues: League[];

onMounted(async () => {
    await customAxios
        .get<League[]>(`account/leagues`, {
            withCredentials: true,
    })
    .then((response) => {
        leagues = response.data;
    });
});
</script>

My custom axios file

import axios from "axios";

export const customAxios = axios.create({
    baseURL: "http://localhost:8080/api/",
});

customAxios.interceptors.response.use(
    (response) => response,
    (error) => {
        if (error.response.status === 401) {
            window.location.href = '/';
        }
        if (error.response) {
            alert(error.response.data.message);
        }
    }
);

export default customAxios;

In the Network tab in Google Chrome I can see the HTTP 200 code and the Response: [{"league_id":1,"league_status":"created","league_name":"Test League","league_code":"Test League Code"}]

However here is the rendered page: Rendered page

If I go back to VSCode and change anything within the , adding a space for example and saving then my page is refreshed and the values are displayed as expected: Rendered image after altering DOM in VSCode

I would like the values to be displayed on page load but can't seem to find how to do it. I am guessing it is not working on first load because it is displaying the empty values of let leagues: League[]; and a change of the DOM forces the values to be taken after the data is fetched but not sure how I can force the DOM to display the values fetched (if that makes sense).

I could not find any example with vue3 and .

Thanks!

CodePudding user response:

As @dan-obregon wrote in his answer, you missed the most fundamental part of Vue, its reactivity. Dan's answer is incomplete as you can't assign the array to the reactive object itself. In this case I would use ref instead:

<script setup lang="ts">
import { Ref, ref, onMounted } from "vue";
import customAxios from "@/lib/customAxios";

interface League {
    league_id: number,
    league_name: string;
    league_code: string;
    league_status: string;
}

const leagues: Ref<Array<League>> = ref([]);

onMounted(async () => {
    await customAxios
        .get<League[]>(`account/leagues`, {
            withCredentials: true,
    })
    .then((response) => {
        leagues.value = response.data;
    });
});
</script>

CodePudding user response:

Maybe it's a good idea to wait for the value leagues somehow. Probably you are getting an error because the component is failing while loading some values of your leagues property that has not been loaded yet. When you save in VSCode, the component has already been mounted and your leagues data has already been fetched so in that case you don't get the error. The first thing that came to my mind:

Create a new v-if inside your component. If your axios response returns a not empty list, then you bind your data. For example, in line 6:

<template lang="">
    <div >
        <div >
            <h3>Leagues</h3>
        </div>
        <div v-if="leagues.length > 0">
            <ul >
                <li
                    v-for="(leagueItem, index) in leagues"
                    :key="index"
                    
                >
                    {{ index   1 }}.{{ leagueItem.league_name }}
                    , Status:
                    {{ leagueItem.league_status }}, Code: {{ leagueItem.league_code }}
                </li>
            </ul>
        </div>
    </div>
</template>

CodePudding user response:

This looks like an issue with reactivity.

Here are a few resources to explain reactivity, if you don't already know:

I would change the declaration of leagues:

import {reactive} from 'vue'
...
const league = reactive<Leagues[]>([])
  • Related