Vue and element ui’s el-checkbox pit

1. The scene of the problem

Traverse an array, use checkbox, click on a tag to achieve selected and unselected status

2. Example of official website

By switching the checked value to true or false, the state switching of a checkbox

 <template>
  <el-checkbox v-model="checked">Alternatives</el-checkbox>
</template>
<script>
  export default {<!-- -->
    data() {<!-- -->
      return {<!-- -->
        checked: true
      }
    }
  }
</script>

3. Thinking and testing

After looping li, add the checked attribute to the data and bind it to v-model to realize a scenario. The template code is as follows:

<template>
  <div class="demo">
    <ul>
      <li v-for="(item, index) in list" :key="index"> //Loop li
        <el-checkbox v-model="item.checked"> //v-model is bound to the checked attribute of each item
          {<!-- -->{<!-- --> item.name }}
        </el-checkbox>
      </li>
    </ul>
  </div>
</template>

The data format returned by the background (simplified) is as follows (no checked attribute)

[
        {<!-- --> id: 1, pid: 1, name: 'region' },
        {<!-- --> id: 2, pid: 2, name: 'Game Type' },
        {<!-- --> id: 3, pid: 4, name: 'gender' },
        {<!-- --> id: 4, pid: 5, name: 'device type' },
        {<!-- --> id: 5, pid: 6, name: 'leisure time' },
        {<!-- --> id: 6, pid: 7, name: 'Glory of the King' },
        {<!-- --> id: 7, pid: 8, name: 'music' },
        {<!-- --> id: 8, pid: 9, name: 'brand watch' },
        {<!-- --> id: 9, pid: 10, name: 'camera' },
        {<!-- --> id: 10, pid: 12, name: 'Game Crowd' },
      ]

What I want to do is to get the background data through the mounted method, and then add the checked attribute to each piece of data in a loop. The initial value is false.

<script>
  export default {<!-- -->
    name: 'demo',
    data() {<!-- -->
      return {<!-- -->
        list: [],
        allTags: [],
      }
    },
    methods: {<!-- -->
      getList() {<!-- -->
        // Get data simulated with settimeout
        setTimeout(() => {<!-- -->
          this.allTags = [
            {<!-- --> id: 1, pid: 1, name: 'region' },
            {<!-- --> id: 2, pid: 2, name: 'Game type' },
            {<!-- --> id: 3, pid: 4, name: 'gender' },
            {<!-- --> id: 4, pid: 5, name: 'Device type' },
            {<!-- --> id: 5, pid: 6, name: 'leisure time' },
            {<!-- --> id: 6, pid: 7, name: 'Glory of the King' },
            {<!-- --> id: 7, pid: 8, name: 'Music' },
            {<!-- --> id: 8, pid: 9, name: 'Brand Watch' },
            {<!-- --> id: 9, pid: 10, name: 'Camera' },
            {<!-- --> id: 10, pid: 12, name: 'Game Crowd' },
          ]
          this.allTags.map(item => {<!-- -->
            item.checked = false
            return item
          })
          this.list = this.allTags
        }, 1500)
      },
    },
    mounted() {<!-- -->
      this.getList()
    },
  }
</script>

At this point, I feel that the function has been implemented. Open the page to see the effect. I found the problem: When I clicked, the emmmmmmmmm was not checked…

Troubleshooting…

 (The painful process of brainstorming...)

  guess:

    1. Element does not support this method of binding. It can only be implemented by looping el-checkbox as shown in the example on the official website.

    2. Vue binding problem

  Regarding question 1: I changed to the loop el-checkbox and found that the result is the same. deny!

  The rest is conjecture two:

        The returned data is artificially added with the checked attribute by default, that is, the data format is:
[
            {<!-- --> id: 1, pid: 1, name: 'region', checked: false},
            {<!-- --> id: 2, pid: 2, name: 'Game type' , checked: false},
            {<!-- --> id: 3, pid: 4, name: 'gender' , checked: false},
            {<!-- --> id: 4, pid: 5, name: 'Device type' , checked: false},
            {<!-- --> id: 5, pid: 6, name: 'leisure time' , checked: false},
            {<!-- --> id: 6, pid: 7, name: 'Glory of the King', checked: false },
            {<!-- --> id: 7, pid: 8, name: 'Music', checked: false },
            {<!-- --> id: 8, pid: 9, name: 'Brand Watch' , checked: false},
            {<!-- --> id: 9, pid: 10, name: 'Camera' , checked: false},
            {<!-- --> id: 10, pid: 12, name: 'Game Crowd' , checked: false},
          ]

Testing found that this works. . .

Confused. . .

     Then print the data on the console and compare the results.

     The status of adding the checked attribute at the front end and the status of the checked attribute in the data returned by the background are printed out

By comparison, it is found that: the front end adds the checked attribute, and vue does not add the get set method, so the change of the checked value cannot be monitored, and the view cannot be updated. This can be seen in the browser Vue debugging. When clicking, the checked attribute true and false of the data are changing alternately, but the view is not updated synchronously.

4. Solution

 Two methods,
    1. The value obtained is not assigned to allTags of the data attribute, but a temporary variable let is defined. After the operation is completed, the value is assigned to the list, namely:
<script>
  export default {<!-- -->
    name: 'demo',
    data() {<!-- -->
      return {<!-- -->
        list: [],
      }
    },
    methods: {<!-- -->
      getList() {<!-- -->
        //Get data and simulate it with settimeout
        setTimeout(() => {<!-- -->
         let allTags = [ //let defines allTags here
            {<!-- --> id: 1, pid: 1, name: 'region' },
            {<!-- --> id: 2, pid: 2, name: 'Game type' },
            {<!-- --> id: 3, pid: 4, name: 'gender' },
            {<!-- --> id: 4, pid: 5, name: 'Device type' },
            {<!-- --> id: 5, pid: 6, name: 'leisure time' },
            {<!-- --> id: 6, pid: 7, name: 'Glory of the King' },
            {<!-- --> id: 7, pid: 8, name: 'Music' },
            {<!-- --> id: 8, pid: 9, name: 'Brand Watch' },
            {<!-- --> id: 9, pid: 10, name: 'Camera' },
            {<!-- --> id: 10, pid: 12, name: 'Game Crowd' },
          ]
          allTags.map(item => {<!-- -->
            item.checked = false
            return item
          })
          this.list = allTags
        }, 1500)
      },
    },
    mounted() {<!-- -->
      this.getList()
    },
  }
</script>

2 (recommended), use the vue.$set method to force vue to monitor the checked attribute

<script>
  export default {<!-- -->
    name: 'demo',
    data() {<!-- -->
      return {<!-- -->
        list: [],
      }
    },
    methods: {<!-- -->
      getList() {<!-- -->
        //Get data and simulate it with settimeout
        setTimeout(() => {<!-- -->
         this.allTags = [ //let defines allTags here
            {<!-- --> id: 1, pid: 1, name: 'region' },
            {<!-- --> id: 2, pid: 2, name: 'Game Type' },
            {<!-- --> id: 3, pid: 4, name: 'gender' },
            {<!-- --> id: 4, pid: 5, name: 'device type' },
            {<!-- --> id: 5, pid: 6, name: 'leisure time' },
            {<!-- --> id: 6, pid: 7, name: 'Glory of the King' },
            {<!-- --> id: 7, pid: 8, name: 'music' },
            {<!-- --> id: 8, pid: 9, name: 'brand watch' },
            {<!-- --> id: 9, pid: 10, name: 'camera' },
            {<!-- --> id: 10, pid: 12, name: 'Game Crowd' },
          ]
          this.allTags.map(item => {<!-- -->
            //item.checked = false
            this.$set(item, 'checked', false) // Here, to add properties to the object, use the $set method.
            return item
          })
          this.list = this.allTags
        }, 1500)
      },
    },
    mounted() {<!-- -->
      this.getList()
    },
  }
</script>

done! !

Five, summary.

 This problem is a problem I encountered in the project. After locking the problem step by step, I solved it. I pretend to summarize this and give a little help to the rest of the children's shoes who encountered pitfalls.