Attempting to change the checked state of a child component from the parent component in a synchronous manner

Please run the demo provided below:

let myCheckbox = Vue.component('my-checkbox', {
  template: `<div>
                <input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}            
            </div>`,
  props: ['checked'],
  methods: {
    change() {
      this.$emit('change', event.target.checked);
    }
  }
})

new Vue({
  el: '#app',
  data: {
    checked: true,
    count: 1
  },
  methods: {
    change(isChecked) {
      this.count++
        this.checked = isChecked
      if (this.count % 2 === 0) {
        // this.checked = !isChecked
        setTimeout(() => {
          this.checked = !isChecked
        }, 10);
      }
    }
  },
  components: {
    myCheckbox
  }
})
span {
  background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <my-checkbox :checked="checked" @change="change"></my-checkbox>
  When
  <span>{{count}}</span> is even your check will fail
</div>

However, changing the synchronous operation to an asynchronous operation in the change method leads to the following:

        setTimeout(() => {
          this.checked = !isChecked
        }, 10);

instead of

         this.checked = !isChecked

will cause the demo to not function properly because the checked state in check1 is not updated.

The question arises:

Why does the synchronous operation not work?

There might be a promise after the change operation triggering the asynchronous behavior, which is why setTimeout is effective. However, no definitive explanation has been found for this behavior.

Answer №1

When you click on the checkbox, it visually changes by adding a tick symbol (v).

After the checkbox is ticked, it triggers the change event.

It is important to note that the Vue's checked property does not get updated immediately. The update occurs when the change event is handled and the checked property is set accordingly. This process is illustrated in line 4 of the code snippet below:

methods: {                      // 1
  change(isChecked) {           // 2
    this.count++;               // 3
    this.checked = isChecked    // 4

When the value of this.count is even and the setTimeout function is not used, the this.checked is immediately set back to its previous value.

Vue recognizes that this.checked did not change after the method execution, so it does not update or repaint the component. This results in the tick symbol (v) staying visible, even though it should not be. This is because Vue did not repaint the component.

You can test this behavior in the demo below. The updated lifecycle method does not execute as expected:

let myCheckbox = Vue.component('my-checkbox', {
  template:
  `<div>
     <input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}            
   </div>`,
  props: ['checked'],
  methods: {
    change() {
      this.$emit('change', event.target.checked);
    }
  },
  // not executed because `checked` never changes (is always true)
  updated() { console.log('my-checkbox updated') }
})

new Vue({
  el: '#app',
  data: {
    checked: true,
    count: 1
  },
  methods: {
    change(isChecked) {
      this.count++;
      this.checked = isChecked
      if (this.count % 2 === 0) {
        this.checked = !isChecked
      }
    }
  },
  components: {
    myCheckbox
  }
})
span {
  background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <my-checkbox :checked="checked" @change="change"></my-checkbox>
  When
  <span>{{count}}</span> is even your check will fail. - checked: <span>{{checked}}</span> - it never changes, always <b>true</b>, so my-checkbox <b>never</b> has to be updated/repainted.
</div>


Using the setTimeout function, the this.checked actually does change once the change(isChecked) method finishes executing. This change triggers the repaint of the component.

However, shortly after the setTimeout handler executes, resetting this.checked back to its original value triggers another repaint. In the demo below, you can see the updated hook being executed twice when this.count is even.

It is worth noting that the recommended approach in Vue is to use Vue.nextTick() instead of setTimeout:

let myCheckbox = Vue.component('my-checkbox', {
  template:
  `<div>
     <input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}            
   </div>`,
  props: ['checked'],
  methods: {
    change() {
      this.$emit('change', event.target.checked);
    }
  },
  updated() { console.log('my-checkbox updated') },
})

new Vue({
  el: '#app',
  data: {
    checked: true,
    count: 1
  },
  methods: {
    change(isChecked) {
      this.count++;
      this.checked = isChecked
      if (this.count % 2 === 0) {
        Vue.nextTick(() => {
          this.checked = !isChecked
        });
        //setTimeout(() => {
        //  this.checked = !isChecked
        //}, 10);
      }
    }
  },
  components: {
    myCheckbox
  }
})
span {
  background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <my-checkbox :checked="checked" @change="change"></my-checkbox>
  When
  <span>{{count}}</span> is even the checked will be overridden. checked: <span>{{checked}}</span>
</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

Generating a download link with an expiration feature in node.js or express for both frontend and backend operations

Hello everyone, I'm a beginner here and have recently started working with node.js and express.js. I am looking to implement a download link on my website that expires after a certain time, but I've been struggling with the code for about a week ...

Is there a way to link to a specific section within a webpage using its ID, landing halfway down the page instead of at the top?

Can someone help me out with this issue? The header on my page is fixed at the top and has a height of 75px. When I click on a link (<a href="/company/#contact/">contact</a>), it takes me to the right section but the top part is hidden behind t ...

Is it possible to dynamically assign and call functions through an Object in Angular 6?

I implemented a click handler for a button that is generated dynamically. Within the click handler function, I am invoking a callback function. However, I encountered an issue where an error message popped up stating that "Callback function is not a fu ...

The method by which npmjs.com evaluates the quality of code

After we release a package on npm, it provides us with insights such as popularity, quality, and maintenance on the search page (Refer to the example image below). I am particularly curious about how npm determines the quality metric. Any insights would be ...

The behavior of having two submit buttons within the $document.ready(function) in Jquery

In my code, I have implemented the behavior of two buttons, button1 and button2, within a $(document).ready(function). Whenever either button is clicked, an alert() function should be triggered. However, it seems that only button2 is functioning properly w ...

The Typescript Select is displaying an incorrect value

Here's the code snippet I've been working with: <select #C (change)="changeSelect(zone.id, C.value)"> <option *ngFor="let town of townsLocal" [attr.value]="town.data" [attr.selected]="town.data === zone.town && 'selected& ...

Troubleshooting problems with integrating Jquery Plugin in Angular for creating dynamic dropdown menus

Currently, I am utilizing the selectric jQuery plugin in conjunction with my Angular dropdown. When I have the dropdown options hardcoded in the HTML, everything functions correctly with just the following code: $('select, .select').selectric() ...

Executing JavaScript code within a Django application to load a file

Utilizing a JavaScript tool called jQuery FileTree within my Django application has presented a dilemma. This particular JavaScript requires access to a python script path, but incorporating Django template tags directly into JavaScript poses an issue. Wi ...

Nodemailer fails to send out emails despite the absence of any error messages

I'm currently working on setting up a confirmation email feature for user sign-ups on my website. I've tackled similar tasks in the past, but this time I've hit a roadblock - the emails are not being sent, and there are no error messages to ...

Vue Leaflet 2.0 utilizes ES modules in order to reduce the size of the

In my Vue-cli 2 project, I have imported Vue2-leaflet modules LMap and LTileLayer in my main.js like this: import { LMap, LTileLayer } from 'vue2-leaflet' Despite following the suggestion to only import the needed modules, the webpack-bundle-ana ...

Add MySQL JSON data to an array

Having an issue with a MySQL query in Node.js. After executing the query and receiving a JSON result, I'm trying to store the query result in an array variable. However, I'm having trouble accessing the elements of the array as it always returns ...

Conceal YouTube upon opening any model or light box

I have a YouTube video in the center of my page, and when I click on any link from the side navigation, a lightbox appears. However, in IE, the lightbox is behind the YouTube video. I have tried setting the Z-index, but it doesn't seem to work. Is the ...

Having trouble displaying json data in an HTML file with d3.js

I am having trouble loading data from a json file into an HTML file and using D3 to visualize it. Even though the file loads correctly and is verified with a 200 status, the contents are interpreted as null. Below are the contents of the json file: [{"to ...

Dynatree fails to consider the select feature while utilizing ajax requests

Currently, I am utilizing the dynatree plugin to exhibit a checkbox tree in multi-select mode (mode 3). Upon initializing the tree using ajax (without lazy loading), it appears that certain nodes that were initially loaded as selected are forgotten. When ...

How can I configure AngularJS intellisense in Visual Studio Code?

I've been customizing Visual Studio Code for better compatibility with our Angular 1.5 codebase at my workplace. Here's the progress I've made so far: Downloaded and installed TSD Ran the command tsd query -r -o -a install angular -s Added ...

No tests were located for execution. Despite our efforts, a cypress.json file was not found in the search

Having recently set up Cypress in my project, I encountered an issue while attempting to run it using npx cypress run. The error message displayed was: Could not find any tests to run. We searched for a cypress.json file in this directory but did not ...

Creating a distinctive appearance for JavaScript's default dialogue box

Is there a way to enhance the design of my code that prompts the user for input using JavaScript's `prompt`? Currently, it appears too simplistic. Are there any CSS or alternative methods to improve its appearance? function textPrompt(){ var text = ...

Enhance Your MUI React Component with Custom Styles and ThemeProvider Integration

Currently, I am delving into the world of MUI components within a React environment. Specifically, I am utilizing MUI 5 and React 17. These MUI components are sourced from a third-party library, resulting in limited direct access. My current goal is to rev ...

What is the best way to create a Laravel object for use in JavaScript in a Laravel Blade?

please add description hereI am looking to obtain an object with this particular style var zoz2= { "11-30--0001": "<a href=\"https:\/\/www.dooz.ps\/p\/107229\" >\u0625\u0637\u0644\u0627& ...

Having trouble with submitting data in an ExpressJS POST request while using mongoose?

As I embark on building my first express.js application, I encounter my initial obstacle. The setup is rather simple. Routes in app.js: app.get('/', routes.index); app.get('/users', user.list); app.get('/products', product. ...