Search code examples
javascriptvue.jsvuejs3nuxt.jsvue-component

Only the last v-for element is updated when all of them should


3 page links in the nav should be displayed in 3 languages by user choice. In the initial page load it works fine. Then click one language change link then again it works fine. But when I click a language change link another time only the last link's language changes when all of them should change language. What is causing this problem and how to fix it?

app.vue:

<template>
    <nav class="navbar">
        <NuxtLink class="pagelink" :key="page.slug" v-for="page in strings.pages" :href="'/' + page.slug">{{ page.name[lang] }}</NuxtLink>
        <Languages />
    </nav>
</template>
<script>
import Languages from "./components/languages.vue"
import languages from "../services/languages"
export default {
    name: "Navbar",
    data() {
        return {
            open: false,
            strings: {
                pages: [
                    {
                        slug: 'home',
                        name: { az: "Əsas", ru: "Главная", en: "Home" }
                    },
                    {
                        slug: 'about',
                        name: { az: "Haqqımızda", ru: "О нас", en: "About" }
                    },
                    {
                        slug: 'contact',
                        name: { az: "Əlaqə", ru: "Связаться", en: "Contact Us" }
                    }
                ]
            }
        }
    },
    computed: {
        lang() {
            return languages(this)
        }
    }
}
</script>

<style>
* {
    margin: 10px;
}
</style>

languages.vue:

<template>
    <div class="languages">
        <NuxtLink :to="route.path + '?hl=az'">AZ</NuxtLink>
        <NuxtLink :to="route.path + '?hl=ru'">RU</NuxtLink>
        <NuxtLink :to="route.path + '?hl=en'">EN</NuxtLink>
    </div>
</template>

<script>
export default {
    name: "Languages",
    setup() {
        const route = useRoute()
        return {
            route
        }
    }
}
</script>

<style scoped>
div,
div a {
    height: 40px;
    display: inline-block;
}

img {
    height: 60%;
    display: inline-flex;
    margin: 8px;
}
</style>

languages.js:

function languages(page) {
    let langCookie = useCookie("language")
    let language = langCookie.value
    if (page.$route.query.hl) {
        language = page.$route.query.hl
        langCookie.value = language
    }
    return language || 'az';
}

export default languages

Solution

  • The primary issue I see with your code is that you are altering the cookie in the languages function, called by your lang computed.

    A computed should never create side effects, see Vue docs for reference.

    You should split your logic and only return the current language or a fallback value in the computed.

    setup() {
        let langCookie = useCookie('language');
    
        return {
          langCookie
        }
    },
    
    computed: {
        lang() {
            return this.langCookie || 'az';
        }
    }
    

    The code to persist the new language in the cookie should be extracted and called only when necessary. You could for example call it when the user click on a language link in the language selector or add an immediate watcher on the language value present in the URL using.

    <template>
      <div class="languages">
        <!-- You don’t need to pass the current URL again here, you can alter the query only, as an object, vue-router will take care of computing the URL -->
        <NuxtLink :to="{ query: { hl: 'az' } }">AZ</NuxtLink>
        <NuxtLink :to="{ query: { hl: 'ru' } }">RU</NuxtLink>
        <NuxtLink :to="{ query: { hl: 'en' } }">EN</NuxtLink>
      </div>
    </template>
    
    <script>
    import { watch } from 'vue';
    import { useRoute } from 'vue-router';
    import { useCookie } from '?';
    
    export default {
      name: 'Languages',
    
      setup() {
        const route = useRoute();
        const langCookie = useCookie('language');
    
        watch(
          // Whenever the route query language change
          () => route.query.hl,
          // Save the new value in the cookie
          (newLang) => {
            langCookie.value = newLang;
          },
          {
            // Do this logic immediately, in case the page is loaded
            // with `hl` already set and different from the current cookie value
            immediate: true
          }
        );
      }
    }
    </script>