Vue.js - v-for not automatically updating when assigning a new array is not functioning

I've been going through a Vue.js tutorial on YouTube: Vue.js Tutorial: Beginner to Front-End Developer https://www.youtube.com/watch?v=1GNsWa_EZdw&t=48s

Most of the tutorial has been working fine for me, except for the issue with updating the navigation list. The instructor mentions using an Eventlistener for page updates around 04:01:20.

I'm facing trouble with assigning a new array to 'this.pages' to update the v-for loop in my code. What could be the reason this is not working and how can I troubleshoot it?

this.$bus.$on('page-updated', () => {
    this.pages = [...this.$pages.getAllPages()];
});

Here are some relevant parts of the script:

Navbar.vue

<template>
    ...
    <navbar-link
        v-for="(page, index) in publishedPages" 
        class="nav-item" 
        :key="index"
        :page="page"
        :index="index"                  
    ></navbar-link>
    ...
</template>

<script>
import NavbarLink from './NavbarLink.vue';

export default {
    components: {
        NavbarLink
    },
    inject: ['$pages', '$bus'],
    created() {
        this.pages = this.$pages.getAllPages();
        this.$bus.$on('page-updated', () => {
            this.pages = [...this.$pages.getAllPages()];
        });
    },
    data() {
        return {
            theme: 'dark',
            data: []
        }
    },
    computed: {
        publishedPages() {
            return this.pages.filter(p => p.published)
        }
    },
    ..
  } 
}
</script>

PadeEdit.vue

<script setup>
import { useRouter } from 'vue-router';
import { inject } from 'vue';
const router = useRouter();
const pages = inject('$pages');
const bus = inject('$bus');
const {index} = defineProps(['index']);
let page = pages.getSinglePage(index);
function submit() {
    pages.editPage(index, page);
    bus.$emit('page-updated', {
        index,
        page
    });
    ...
}
...
</script>

Events.js

const events = new Map();
export default {
    $on(eventName, fn) {
        if (!events.has(eventName)) {
            events.set(eventName, [])
        }
        events.get(eventName).push(fn);
    },
    $emit(eventName, data) {
        if (events.has(eventName)) {
            events.get(eventName).forEach(fn => fn(data));
        }
    }
};

data.js

const pagesKey = 'pages';

let pagesJson = localStorage.getItem(pagesKey);
let pagesStore = JSON.parse(pagesJson);

function save() {
    localStorage.setItem(pagesKey, JSON.stringify(pagesStore));
}

export default {
    addPage(page){
        pagesStore.push(page);
        save();
    },
    getAllPages() {
        return pagesStore;
    },
    getSinglePage(index) {
        return pagesStore[index];
    },
    editPage(index, page) {
        pagesStore[index] = page;
        save();
    }
}

Answer №1

Brief answer:

In your Navbar.vue file, you need to update the data() method like this:

data() {
    return {
        theme: 'dark',
        pages: [], // <--- previously was data: []
        counter: 0
    }
},

Detailed answer:

Upon thorough debugging of the entire Navbar.vue component, it became evident that the issue lies in the computed property referencing an incorrect this.pages

publishedPages() {
    return this.pages.filter(p => p.published);
}

The reference to this.pages above actually originates from the created() hook:

created() {
    this.getThemeSetting();

    this.pages = this.$pages.getAllPages();          
    ...
}

However, this.pages defined here is not a reactive variable. Consequently, when it updates, the computed property does not recompute.

This inconsistency may lead one to ponder, "Why does it work initially?".

The reason behind its initial functionality is that computed properties execute once during component creation to obtain their initial values, and by directly assigning pages to this within the statement this.pages = ..., everything functions as expected during initialization. Nevertheless, since this.pages within the created() hook lacks reactivity, Vue does not monitor changes to it, causing the computed property to remain static post-initialization. Only variables defined inside data() exhibit reactivity.

The remedy entails adding pages to data()

data() {
    return {
        theme: 'dark',
        pages: [],  // <--- originally cited as "data: []"
        counter: 0
    }
},

Henceforth, any mention of this.pages will allude to this reactive data variable. Subsequently, the remaining code shall operate optimally. Therefore, upon execution of:

this.pages = [...this.$pages.getAllPages()];

The data variable will be refreshed, and owing to pages functioning as a reactive dependency of the computed property publishedPages, the latter will recalculate. Ultimately, the template's v-for loop will refresh due to the updated computed property value:

<navbar-link v-for="(page, index) in publishedPages" />

P.S. The transition from data[] to pages[] occurs in the tutorial video between 3:22:00 and 3:22:30

The creator first defines the data as data: [] but swiftly amends it to pages: [] moments later. This adjustment might have been inconspicuous while following along with the tutorial.

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

What can I do to condense the length of b-dropdown-item-button or make the text wrap instead?

DropDown Currently, I am utilizing Bootstrap-vue. My goal is to ensure that the b-dropdown-item-button wraps the text it displays. Is there a recommended approach for achieving this? <template> <b-dropdown menu-class="py-1" text="··· Action ...

Mongoose encountering an issue with undefined properties (specifically 'prototype') and cannot read them

I am currently using Mongoose 7.0.1 on Next JS 13.2.2 and React 18.2.0. After tirelessly searching for a solution to my problem, I am still struggling with connecting to a MongoDB. Every time I try to import mongoose into my project for the connection, an ...

Update the state when a button is clicked and send a request using Axios

Currently in my front end (using react): import '../styles/TourPage.css'; import React, { Component } from 'react'; import axios from 'axios' class TourPage extends Component { constructor(props) { super(p ...

Display an image in a pop-up when hovering over it briefly

When you run the code snippet and hover over "Hover here", a picture of grumpy cat will appear, but the image flashes on and off repeatedly: To see the image consistently, you may need to move your cursor away from "Hover here" and hover over it again. ...

Organize items in a linear array by their initial letter

I'm working with an array containing strings. $cities = ['New York', 'Boston', 'Los Angeles', 'Cincinnati', 'Chicago', 'Houston', 'Philadelphia', 'Dallas', 'Seattle&a ...

Visualize data from ajax call in tabular format

I want to display the results of an SQL query in a table using AJAX. I have written code that executes the query during the AJAX call, but when I try to display these values in a table, it shows null values on the div tag. What could be the reason for this ...

Manipulating Objects and Arrays

When I retrieve data from a database query in Node.js using Postgres with Knex, I get an array of objects structured like this: (condensed version) [ { tvshow: 'house', airdate: 2017-02-01T00:00:00.000Z }, { tvshow: ' ...

Is there a way to obtain an automatically generated ID when using the add() method?

Currently, I am working with node, vue, and firestore in my project. One of the requirements is to have a page where the URL corresponds to the auto-generated ID of each document created using the add() method. In order to achieve this functionality, I i ...

Running into issues on Heroku with Node, Gulp, and Browserify: Module Not Found Error

My asset building process relies on Gulp. Specifically for Javascript, I have a task set up with Browserify to handle all code dependencies. While everything runs smoothly locally, deploying to Heroku results in a Gulp failure with the following error: 2 ...

VueJS + Laravel 5.4 - Mix: "Error 404 - Page Not Found"

After downloading and starting with a Laravel + Vue component known as vuetable-2 (which is quite nice), I encountered some issues. Initially, running php artisan serve seemed to work fine, except for the fact that changes made to .vue files were not hot ...

Designated location for downloading specified by the user

I've been searching for a while now and I can't seem to find a satisfactory answer. Essentially, I have a website where users can input values and download a specific file based on those values. Everything is functional, but I want to give the u ...

Do you only need to utilize Provider once?

When using the redux module in react-native, it is common practice to utilize createStore from 'redux'. I am curious, is it sufficient to use <Provider/> just once to make the Redux store accessible throughout our app? import ReactDOM from ...

While using axios to make a GET request, I encountered errors when checking for .isSuccess in react

const searchInvoiceList = async ( plantLocation: string, invoiceType: string ) => { let dataList: InvoiceData[] = []; await axios .get(`${linkURL}inv/getControlList/${plantLocation}/${invoiceType}`) .then((response) => { dataLis ...

Looking for a way to make one image disappear briefly while transitioning to another image in Javascript

**Hey there, coding enthusiasts! I could really use some help right now. I am trying to create a smooth image transition effect using Javascript, where one image fades out while the next one fades in seamlessly. As someone who is still relatively new to pr ...

Transform an object into an array of objects by adding extra properties to each one

The following code snippet represents data in the form of an object with each key containing a date. The properties within the object include Open and Closed. If the value for Closed is 0, then that property is not included. let data = { "20 ...

Tips for accessing array values dynamically in Vuejs from an existing array?

WelcomeWorld.vue export const dataList = [ { id: 1, val: "11", kk: "potter" }, { id: 2, val: "22", kk: "james" }, { id: 3, val: "55", kk: "limda" }, { id: 4, val: "77", kk: "stepen" } ]; <template> <div> <b>Vuejs dyn ...

Angular 2: Firebase fails to provide a response

I am facing an issue with retrieving data from my Firebase using Angular 2 and TypeScript. While console.log() works, I am unable to return the value into a variable. My DataService looks like this: import {Injectable} from "angular2/core"; import ' ...

Troubleshooting email form validation issues in Vue.js

Even after entering the email id, I am still receiving the error message stating that the email is required. Here is a screenshot for reference: . I want the error message to be hidden when the email id is entered. HTML: <form class="footer-f ...

Display SVG at full size without any distortion

How can I make the SVG image 100% by 100% without scaling? I want the image to be centered on the page both horizontally and vertically. The challenge is that I also want to display the areas outside of the SVG artboard, so using CSS to center it won&apos ...

VueJs Multiselect - displaying single option

I'm having an issue with VueJS Multiselect where once I select an option from the drop-down menu, the other options disappear. How can I enable selecting multiple options? Additionally, is there a way to hide the square brackets [] that are displayed ...