Switch the monitored variable's value in VueJS

` `

I have a data variable that is of boolean type, and I have included this in a watch hook. When the value changes to true, I execute certain tasks within the watch function and everything works perfectly.

` `
watch: {`
`   boolVar: function (val) {`
`     if(val === true){`
`       //performing some tasks`
`     }`
`   }`
`}`
`
` `

Later, I needed to add additional functionality where I had to toggle the variable if it was already set to true in order to re-trigger the watcher. If at a certain point, boolVar is already true and I need the watcher to run again, what would be the best approach?

` `

Initially, I tried toggling the boolVar like this:

` `
//inside another function`
`if(this.boolVar === true){`
`  this.boolVar = false;`
`  this.boolVar = true;`
`}`
`
` `

However, this approach did not work as expected. After some experimentation, I discovered that there was a difference in the time delay at which the watcher was listening, causing the code to not function as intended.

` `

I then implemented a different method using the sleep function and promises to introduce a 1 millisecond delay so that the watcher could listen properly, and it worked according to my requirements:

` `
//inside another function`
`//making the function async to use await`

`if(this.boolVar === true){`
`  this.boolVar = false;`
`  await this.sleep(1);`
`  this.boolVar = true;`
`}`
`
` `
//sleep function`
`sleep(ms) {`
`      return new Promise((resolve) => setTimeout(resolve, ms));`
`},`
`
` `

Is there a better approach to handle this scenario, where you need to toggle a variable being watched and re-run tasks? While my current workaround is functional, I believe the watcher should inherently detect when the same variable is toggled (which it currently does not).

` `

Answer №1

If you're looking to optimize your code, consider using Vue.$nextTick.

if(this.boolVar === true){
  this.boolVar = false;
  this.$nextTick(() => {
    this.boolVar = true;
  })
}

Alternatively, if you prefer not to use $nextTick, you can implement it this way:

  watch: {
    boolVar: {
      handler(val) {
        console.log(val)
      },
      deep: true
    }
  }

Another approach could be creating a specific function, like doSomeWork(), and calling it within your watcher instead of toggling the boolean. This might provide a more logical structure for your code:

watch: {
   boolVar: function (val) {
     if(val === true){
       this.doingSomeTasks()
     }
   }
}

Additionally:

if(this.boolVar === true){
  this.doingSomeTasks()
}

Answer №2

Your code isn't functioning as expected because you assume that all watch handlers run synchronously, but they don't. Vue utilizes an Async Update Queue

When a data change is detected, Vue creates a queue and stores all the data changes that occur in the same event loop. If the same watcher is triggered multiple times, it only gets added to the queue once. This de-duplication process helps prevent unnecessary calculations and DOM manipulations. Subsequently, in the next event loop “tick”, Vue flushes the queue and performs the necessary (de-duped) tasks.

So, when you execute

this.boolVar = false; this.boolVar = true;
, both changes are merely being "registered" by Vue. After all your code finishes executing, the "next event loop" begins - Vue checks the queue for changes, finds the registered watcher, and compares the old value (from the last handler execution) to the new value. In your case, both are true, so Vue sees no actual change and doesn't execute the handler.

The proper technical solution is to delay the second change (this.boolVar = true;) to a later "tick", so that Vue recognizes the false 1st and triggers the watcher in the next tick (from false to true).

However, good code not only works but also clearly communicates the programmer's intent and is easily understandable. The code

this.boolVar = false; this.boolVar = true;
violates this principle significantly.

If you need to perform someTasks when your variable transitions from false to true, that's fine. But you also want to run someTasks in other scenarios where the variable is already true. It's obvious that someTasks should not reside in the watcher, but should be extracted to a standalone method as @Julien suggested...

Update ...just to be complete

What you're seeking is achievable in Vue 3 with the new flush option of the watch - simply use the value

sync</code, and your watcher will be executed synchronously whenever the value changes. Check out the demo below...</p>
<p><div>
<div>
<pre class="lang-js"><code>const app = Vue.createApp({
  data() {
    return {
      value: true,
      flushMode: 'sync',
      unwatch: null
    }
  },
  methods: {
    toggle() {
      this.value = false
      this.value = true
    }
  },
  watch: {
    flushMode: {
      handler: function() {
        this.unwatch && this.unwatch()
        this.unwatch = this.$watch('value', 
          function(newValue) {
            console.log(`New value = ${newValue}`)
          }, 
          { flush: this.flushMode }
        )
      },
      immediate: true    
    }
  },
  updated() {
    console.log('Component updated')
  }
})

app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.1.1/vue.global.prod.min.js" integrity="sha512-vRIbP8hGYYKnayuLhVRwQXTxvAJ/wUm4vqIEzIBKLfDcM2ArSENuojG1jEIactQ7F/0unOe9VQ+U9KagTEyQvA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div id="app">
  <div>
    Variable: {{ value }}
  </div>
  <button @click="toggle">Toggle</button>
  <div>
    <label for="pre">Flush mode:</lael>
    <input type="radio" id="pre" value="pre" v-model="flushMode" /> pre (default)
    <input type="radio" id="sync" value="sync" v-model="flushMode" /> sync
  </div>
</div>

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

A comprehensive guide to leveraging synchronous execution of setTimeout in JavaScript

Can the desired output shown below be obtained using setTimout? If it is possible, please provide your insight: console.log("1st"); setTimeout(() => { console.log("2nd"); },0); console.log("3rd"); The expected output should be: 1st 2nd 3rd ...

In Javascript, where are declared classes stored?

When working in a browser environment like Firefox 60+, I've encountered an issue while attempting to retrieve a class from the global window object: class c{}; console.log(window.c); // undefined This is peculiar, as for any other declaration, it w ...

What is the best way to filter out specific data fields from console.log in JavaScript?

When working with Java, I often use lombok to exclude certain fields from being printed. For instance, the @ToString.Exclude annotation can be used to prevent printing the user token. import lombok.ToString; public class TokenResponse { @ToString.Excl ...

Guide to developing a manual tally counter with recorded logs using HTML and JavaScript

I am currently in need of assistance with creating a manual input counter using the App Script Editor. My website design already includes a single input textbox, a reset button, and a disabled box. What I would like to achieve is that when I enter a numb ...

What is causing the issue of subdomains not functioning properly in express.js?

Currently, I am conducting some local experiments and have made changes to my hosts file. Here are the entries: 127.0.0.1 example.dev 127.0.0.1 www.example.dev 127.0.0.1 api.example.dev Below is the code I am using: var subdomain = req ...

Is it possible for XSS attacks to exploit the HREF attribute when used with jQuery?

Burp suite displaying an error message. The application seems to have a potential vulnerability related to DOM-based cross-site scripting. Data is retrieved from the location and passed to jQuery() using the following statement: jQuery(location). ...

The element in Vue 3 Unit Test is proving difficult to access

I am currently working on a Vue 3 project that utilizes PrimeVue. Within my template, I have integrated a PrimeVue dialog component as shown below: <template> <div> <Button data-testid="showButton" label="Show" @cli ...

Setting up a Laravel 7 and Vue single page application on a shared hosting environment

Hello, I'm facing an issue with my application that has a Vue front-end and Laravel 7 back-end. The problem is that I am unable to redirect to the login page and instead, it shows a blank screen. All the Vue files are stored in the resource/js folder ...

Consolidate multiple generic items into a single entry

In my current project, I am structuring the types for a complex javascript module. One of the requirements is to handle multiple types using generics, as shown in the snippet below: export interface ModelState< FetchListPayload, FetchListR ...

Tips for utilizing jQuery to identify an image that is being hovered on?

Concept My goal is to create an effect where a user hovers over an image and a transparent overlay div appears on top of it. This overlay div starts with a height of 0px and should increase to half of the image height upon hover. The hover functionality ...

I'm looking for a way to convert an array value to JSON using jQuery

i need assistance in converting an array value into json format. An example is provided below: Sample Array [Management Portal!@!@Production Issue Handling!@!@/IONSWeb/refDataManagement/searchDynamicScripts.do, Management Portal!@!@ Event Browser!@!@/ION ...

React-query: When looping through useMutation, only the data from the last request can be accessed

Iterating over an array and applying a mutation to each element array?.forEach((item, index) => { mutate( { ...item }, { onSuccess: ({ id }) => { console.log(id) }, } ); }); The n ...

Transmit a data element from the user interface to the server side without relying on the

I have developed a MEAN stack application. The backend of the application includes a file named api.js: var express = require('express') var router = express.Router(); var body = 'response.send("hello fixed")'; var F = new Function (" ...

Tips for iterating through nested objects with a for loop

Struggling with validations in an Angular 5 application? If you have a form with name, email, gender, and address grouped under city, state, country using FormGroupname, you might find this code snippet helpful: export class RegistrationComponent implemen ...

Dual Camera Toggle Functionality with Three.js Orbit Controls

I am facing an issue with two cameras in one scene, one viewport, and one renderer. I switch between cameras using HTML buttons. ISSUES Issue 1 When using camera1, there is no response from moving the mouse. However, when I switch to camera2, the orbit ...

PHP's 'include' function is now being ported into modern Javascript as a new method

As the library of JS frameworks continues to expand, I'm wondering if there is a simple JS replacement or alternative for PHP's 'include' function. Is PHP include still a relevant method for including chunks of code, or are there better ...

What is the best way to incorporate an AJAX GET request into an HTML element?

Currently, I am attempting to execute a JavaScript code that will convert all <a></a> elements found within another element <b></b> (the specific name in the HTML) into links that trigger an HTTP get request. However, the code I hav ...

Guidelines on Importing Azure App Service Application Settings into VUE

Our Vue app is currently hosted on Azure App Service. In the Azure Portal, we have configured application settings such as VUE_APP_API_ENDPOINT_URL under Settings\Configuration. According to the documentation, these settings become environment variabl ...

Tracking in node.js to detect the creation of a new file

I'm currently working on a node.js script that requires immediate action upon the creation of a file with a specific name in a designated directory. While one way to achieve this could be through continuous calls to fs.readdir until the desired file i ...

Removing buttons from a table row dynamically

Below is how I am adding the Button to Element: (this.sample as any).element.addEventListener("mouseover", function (e) { if ((e.target as HTMLElement).classList.contains("e-rowcell")) { let ele: Element = e.target as Element; let ro ...