"The Vue component's data is successfully updating within a method using setInterval(), however, the changes are not reflected in the DOM

I have a function in my methods that gets triggered with a @click in the view. The function starts a timer, and although it seems to work fine in the DevTools, the timer only updates in the DevTools when I refresh the page.

Moreover, in the view, the timer is not displayed at all because it's rendered only once and doesn't seek updates.

UPDATED: Full code on request

<template>
<div class="container-fluid">
    <div class="card mt-2 p-3 w-75 mx-auto">
        <h2>League Summoners Timers</h2>
        <hr>
        <div>
            <h5>Enemy team</h5>
            <div class="row">
                <div class="col-md-12 col-lg-2" v-for="index in lanes" :key="index">
                    <div class="card">
                        <div class="card-header">
                            <p class="m-0">{{ index }}</p>
                        </div>
                        <div class="card-body">
                            <div v-show="!gameStarted">
                                <div v-show="selected[index.toLowerCase()].length < 2" class="spell-image-row" v-for="spell in spells" :key="spell.name" @click="onClickSelection(spell, index)">
                                    <img class="spell-image m-1" :alt="spell.name" :src="spell.image">
                                </div>
                                <div v-show="selected[index.toLowerCase()].length >= 2">
                                    <span class="alert alert-primary">Spells selected</span>
                                    <ul class="mt-3">
                                        <li v-for="selectedSpell in selected[index.toLowerCase()]" :key="selectedSpell">
                                            {{ selectedSpell }}
                                        </li>
                                    </ul>
                                </div>
                            </div>
                            <div v-show="gameStarted">
                                <div v-for="spell in selected[index.toLowerCase()]" :key="index+spell">
                                    <div class="row">
                                        <img style="float: left" class="my-1" :src="spells[spell.toLowerCase()].image" :alt="spell.name" @click="onClickStartTimer(spell, index)">
                                        <p>{{ timers[index.toLowerCase()][spell.toLowerCase()] }}</p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="mt-3">
            <button class="btn btn-primary mr-3" @click="gameStarted = true" v-show="checkSelected() && !gameStarted">Start game</button>
            <button class="btn btn-danger" onClick="window.location.reload();">Reset</button>
        </div>
    </div>
</div>

<script>
    export default {
        name: "LeagueTimers",
        data() {
            return {
                gameStarted: false,
                lanes: ['Top', 'Jungle', 'Mid', 'ADC', 'Support'],
                spells: {
                    teleport: {
                        name: 'Teleport',
                        image: require('../assets/images/Teleport.png'),
                        cooldown: 420,
                    },
                    ignite: {
                        name: 'Ignite',
                        image: require('../assets/images/Ignite.png'),
                        cooldown: 180,
                    },
                    heal: {
                        name: 'Heal',
                        image: require('../assets/images/Heal.png'),
                        cooldown: 240,
                    },
                    ghost: {
                        name: 'Ghost',
                        image: require('../assets/images/Ghost.png'),
                        cooldown: 300,
                    },
                    flash: {
                        name: 'Flash',
                        image: require('../assets/images/Flash.png'),
                        cooldown: 300,
                    },
                    exhaust: {
                        name: 'Exhaust',
                        image: require('../assets/images/Exhaust.png'),
                        cooldown: 210,
                    },
                    cleanse: {
                        name: 'Cleanse',
                        image: require('../assets/images/Cleanse.png'),
                        cooldown: 210,
                    },
                    barrier: {
                        name: 'Barrier',
                        image: require('../assets/images/Barrier.png'),
                        cooldown: 180,
                    },
                    smite: {
                        name: 'Smite',
                        image: require('../assets/images/Smite.png'),
                        cooldown: 15,
                    },
                },
                selected: {
                    top: [],
                    jungle: [],
                    mid: [],
                    adc: [],
                    support: [],
                },
                timers: {
                    top: {},
                    jungle: {},
                    mid: {},
                    adc: {},
                    support: {},
                },
            }
        },
        methods: {
            onClickSelection(spell, lane) {
                this.selected[lane.toLowerCase()].push(spell.name);

            },
            onClickStartTimer(spell, lane) {
                if (!this.timers[lane.toLowerCase()][spell.toLowerCase()]) {
                    this.startTimer(spell.toLowerCase(), lane.toLowerCase(), this.spells[spell.toLowerCase()].cooldown);
                } else {
                    console.log('runt al');
                }
            },
            checkSelected() {
                for (let [key] of Object.entries(this.selected)) {
                    if (this.selected[key].length !== 2) {
                        return false;
                    }
                }
                return true;
            },
            startTimer(spell, lane, cooldown) {
                this.timers[lane][spell] = cooldown;
                let timers = this.timers;
                setInterval(function (){
                    timers[lane][spell]--;
                    // console.log(lane+spell + " - " + timers[lane][spell]);
                    // console.log(typeof timers[lane][spell])
                    this.$forceUpdate();
                }, 1000);
            },
        },
    }
</script>

I have tried using watch: {} and computed: {}, but nothing has worked yet. It's worth mentioning that I'm fairly new to Vue.js.

Answer №1

Ensure your setInterval function is defined correctly by following this format:

 setInterval(() => {
    this.timers[lane][spell]--;
    console.log(lane+spell + " - " + timers[lane][spell]);
    console.log(typeof timers[lane][spell])
    // this.$forceUpdate();
  }, 1000);

By using an arrow function, you can access the property data with this.timers. Using Function syntax will cause a new scope to be created, rendering this.timers inaccessible.

If you prefer using the function syntax, ensure to bind the this object as shown below:

  setInterval(function () {
    this.timers[lane][spell]--;
    console.log(lane+spell + " - " + timers[lane][spell]);
    console.log(typeof timers[lane][spell])
  }.bind(this), 1000)

To maintain reactivity for objects within the data property timers, they must be initialized in advance:

timers:
  { top:
    { teleport: 0,
      smite: 0, 
      ... },
    ...
   }

If you wish to dynamically add properties, utilize the vue set property method. Learn more about it here: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

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

JavaScript: Understanding the concept of closure variables

I am currently working on an HTML/JavaScript program that involves running two counters. The issue I am facing is with the reset counter functionality. The 'initCounter' method initializes two counters with a given initial value. Pressing the &a ...

Why is the Slick Slider displaying previous and next buttons instead of icons?

I'm currently working on creating a slider using the slick slider. Everything seems to be functioning properly, but instead of displaying arrows on the sides, it's showing Previous and Next buttons stacked vertically on the left side. It appears ...

Unexpected behavior: Controller action method retrieves undefined value upon jQuery Ajax request

I'm currently working on my ASP.NET Core 3.1 project and implementing cascading dropdown functionality with jQuery. I've set it up so that changing the value of the first dropdown (Region) should automatically update the second dropdown, Location ...

UI Router: Easily navigate to a specific route by entering the URL directly

I encountered what I thought would be a common issue, but my search turned up empty. I have two states - one accessed at /route and the other at /route/{name}. Everything functions properly when I navigate to the second route using ui-sref, however, if I r ...

What is Angular's approach to handling elements that have more than one directive?

When an element in Angular has multiple directives, each specifying a different scope definition such as scope:false, scope:true, or scope:{}, how does the framework handle this complexity? ...

Anticipating the resolution of the $rootScope value in Angular before the page is fully loaded

I've encountered an issue while using ngView with a static navigation bar that persists throughout the application: <div ng-include="'views/nav.html'" ng-controller="NavCtrl"></div> <div class="container-fluid" ng-view=""> ...

populate vueJS table with data

I encountered an issue while trying to load data from the database into my table created in VueJS. I have set up my component table and my script in app.js, but I am seeing the following error in the view: [Vue warn]: Property or method "datosUsuario" ...

Invoking a Node.js function using a URL reference

Is there a way to call a function located in a remote URL? I have the page URL and the function name. The server-side application is running on NodeJs Express, and the function structure is as follows: function executer(param1, param2){ //logic re ...

Instructions for integrating content into Vuetify's v-navigation-drawer

I stumbled upon this amazing codepen: https://codepen.io/carl_/pen/QWwgqBa that almost does what I need it to do. However, the issue is that there is no content or text displayed on the right side of the menu. I've tried searching for solutions online ...

Can someone please share a straightforward method for dynamically rendering views in Vue.js?

Let's pause and consider the scenario: You're creating a modular interface, where any module that implements it must be able to 'render itself' into the application using a slot and state. How can you achieve this in Vue? An Example S ...

Having trouble using angular.isString in conjunction with ng-repeat

Is there a way to use angular.isString for comparison within an ng-if in an ng-repeat loop? Currently, all items in the array are being returned. I attempted to simply display the result of angular.isString, but no output is generated. This is my desired ...

Retrieve information from a JSON file containing multiple JSON objects for viewing purposes

Looking for a solution to access and display specific elements from a JSON object containing multiple JSON objects. The elements needed are: 1) CampaignName 2) Start date 3) End date An attempt has been made with code that resulted in an error displayed ...

Using Vuex in the router: A comprehensive guide

I am having trouble accessing data from the store in the router. I have attempted three different methods, but none of them seem to be working correctly. Here are the methods I tried: // ReferenceError: store is not defined console.log(store.state); // ...

Attempting to access a particular nested page poses a challenge with Relative Path in VueJS / Laravel, especially when using Router History mode

Hey there! I have a blog site at www.blog.com as my root URL and I'm wondering about how to handle image paths. When linking images for the root URL, I use: For the root URL URL: www.blog.com Resource Path : public/img/blog-logo.png <img :src ...

Creating a plotly.js integration for nuxt SSR: A step-by-step guide

Currently facing issues with setting up plotly.js in my nuxt project. Despite trying various approaches, I keep encountering the error message self is not defined. Installing both plotly.js and plotly.js-dist did not resolve the issue. My attempt to creat ...

Error encountered when using the module export in Node.js

I have a file named db.js which contains the following code: var mysql = require('mysql2'); var mysqlModel = require('mysql-model'); var appModel = mysqlModel.createConnection({ host : 'localhost', us ...

The code malfunctions following the maintenance

Recently, I started learning JavaScript and trying to improve the readability of my code by moving away from inline functions. I have a piece of code that iterates through a JSON array containing comments and then appends those comments to the DOM. Strange ...

What is preventing bots and crawlers from detecting Next.js meta tags?

I'm currently using Next.js with Typescript and MongoDB to retrieve data. I'm encountering difficulties in rendering the page because the crawler is unable to detect the meta tags. For instance, Bing Search Engine claims that Title and Meta desc ...

Having trouble with installing Laravel Bootstrap-vue due to the error message "Cannot read property 'extend' of undefined"?

Experiencing an issue with my Laravel project after adding the bootstrap-vue packages. I followed the instructions on how to set it up from . The versions I am using are "vue": "^2.6.10" and bootstrap vue v2.0.0-rc.17. Below is my webpack.mix.js code: co ...

Playback on iPhone devices and Safari experiences a 50% reduction with AudioWorklet

I recently developed a basic audio recorder that utilizes the AudioWorkletAPI. While the playback functions smoothly on Chrome, it seems to have issues on Safari and iPhone devices (including Chrome on iPhone) where half of the audio is missing. Specifical ...