:class 在 vuejs v-for 中单击时不更新

:class not updating when clicked in vuejs v-for

我想在单击 td 时向 v-for 中的项目添加 css class

      <template>
  <div>
    <h1>Forces ()</h1>

    <section v-if="errored">
      <p>We're sorry, we're not able to retrieve this information at the moment, please try back later</p>
    </section>

    <section v-if="loading">
      <p>loading...</p>
    </section>

    <table class="table table-bordered table-hover">

      <thead>
        <tr>
          <th>#</th>
          <th>ID</th>

          <th 
              @click="orderByName = !orderByName">Forces</th>
        </tr>
        <th>Delete</th>
      </thead>
      <tbody>
        <tr v-for="(force, index) in forces" @click="completeTask(force)" :class="{completed: force.done}"  v-bind:key="index">

          <td>
            <router-link :to="{ name: 'ListForce', params: { name: force.id } }">Link</router-link>
          </td>

          <th>{{ force.done }}</th>

          <th>{{ force.name }}</th>
          <th><button v-on:click="removeElement(index)">remove</button></th>

        </tr>
      </tbody>
    </table>
    <div>
    </div>
  </div>

</template>


<script>
import {APIService} from '../APIService';
const apiService = new APIService();
const _ = require('lodash');

export default {

  name: 'ListForces',

  components: {

  },

  data() {
    return {
      question: '',
      forces: [{
        done: null,
        id: null,
        name: null
      }],
      errored: false,
      loading: true,
      orderByName: false,
    };

  },

  methods: {
    getForces(){
      apiService.getForces().then((data, error) => {

          this.forces = data;
          this.forces.map(function(e){
               e.done = false;
          });
          this.loading= false;
        console.log(this.forces)
      });
    },
    completeTask(force) {
      force.done = ! force.done;
      console.log(force.done);
    },
    removeElement: function (index) {
      this.forces.splice(index, 1);
    }
  },

  computed: {
/*    forcesOrdered() {
      return this.orderByName ? _.orderBy(this.forces, 'name', 'desc') : this.forces;
    }*/
  },
  mounted() {
    this.getForces();
  },
}
</script>

<style>
    .completed {
        text-decoration: line-through;
    }
</style>

我想要一条线在单击时通过项目,但 dom 没有更新。如果我进入 chrome 中的 vue 选项卡,我可以看到每个项目的 force.done 更改,但前提是我单击对象并单击返回对象。我不确定为什么在我之前使用点击和 css 绑定时状态没有更新。

确保 force.done 在初始化前 data 中设置为 false,以便它是反应式的。

data() {
  return {
    force:{
      done: false;
    }
  }
}

如果force存在但没有done成员集,您可以使用Vue.set代替=在初始化后创建一个反应值。

Vue.set(this.force, 'done', true);

我已尝试进行最少的更改以使其正常运行:

// import {APIService} from '../APIService';
// const apiService = new APIService();
// const _ = require('lodash');

const apiService = {
  getForces () {
    return Promise.resolve([
      { id: 1, name: 'Red' },
      { id: 2, name: 'Green' },
      { id: 3, name: 'Blue' }
    ])
  }
}

new Vue({
  el: '#app',
  name: 'ListForces',

  components: {

  },

  data() {
    return {
      question: '',
      forces: [{
        done: null,
        id: null,
        name: null
      }],
      errored: false,
      loading: true,
      orderByName: false,
    };

  },

  methods: {
    getForces(){
      apiService.getForces().then((data, error) => {
          for (const force of data) {
            force.done = false;
          }

          this.forces = data;
          this.loading= false;
        console.log(this.forces)
      });
    },
    completeTask(force) {
      force.done = ! force.done;
      console.log(force.done);
    },
    removeElement: function (index) {
      this.forces.splice(index, 1);
    }
  },

  mounted() {
    this.getForces();
  }
})
.completed {
  text-decoration: line-through;
}
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<div id="app">
  <div>
    <h1>Forces ()</h1>

    <section v-if="errored">
      <p>We're sorry, we're not able to retrieve this information at the moment, please try back later</p>
    </section>

    <section v-if="loading">
      <p>loading...</p>
    </section>

    <table class="table table-bordered table-hover">

      <thead>
        <tr>
          <th>#</th>
          <th>ID</th>

          <th 
              @click="orderByName = !orderByName">Forces</th>
          <th>Delete</th>
        </tr>
        
      </thead>
      <tbody>
        <tr v-for="(force, index) in forces" @click="completeTask(force)" :class="{completed: force.done}"  v-bind:key="index">

          <th>{{ force.done }}</th>

          <th>{{ force.name }}</th>
          <th><button v-on:click="removeElement(index)">remove</button></th>

        </tr>
      </tbody>
    </table>
    <div>
    </div>
  </div>
</div>

关键问题在这里:

this.forces = data;
this.forces.map(function(e){
  e.done = false;
});

这里的问题是,属性 done 在数据变为反应式后被添加到数据中。一旦 this.forces = data 行运行,反应性观察器就会被添加。之后添加 done 算作添加新的 属性,这将不起作用。

这也是对 map 的误用,所以我将其切换为 for/of 循环。

for (const force of data) {
  force.done = false;
}

this.forces = data; // <- data becomes reactive here, including 'done'

在一个不相关的注释中,我调整了模板以将 Delete header 移到顶行。