首页 > 解决方案 > VueJS - 查看模型、孩子和更新值

问题描述

我正在尝试学习 VueJS,但遇到了一个非常基本的问题。我在 Knockout 和 Mustache 方面有很多经验,我认为这可能是导致我出现问题的原因。

我正在尝试构建一个简单的复选框列表来使用绑定,但我的模型没有更新,并且我在控制台中收到以下警告:

避免直接改变 prop,因为只要父组件重新渲染,该值就会被覆盖。相反,使用基于道具值的数据或计算属性。道具被变异:'checked'

这是我正在尝试做的事情;

一个表示待办事项的简单视图模型:

function vmToDoItem(id, title){
  this.id = id;
  this.title = title;
  this.complete = false;
} 

一个表示所有待办事项的简单视图模型:

function vmToDo(items){
  var self = this;

  self.currentFilter = 'all';
  self.items = items || [];

  self.filters = {
    all: function(){
      return self.items;
    },
    active: function(){
      return self.items.filter((item) => !item.complete);
    },
    complete: function(){
      return self.items.filter((item) => item.complete);
    }
  }
}

表示待办事项的组件:

Vue.component('checkbox-item',
             {
                props: ['text', 'checked'],
                template: '#checkbox-item-template',
                computed: {
                  prefix: function(){
                    return !this.checked ? 'fa-circle' : 'fa-check-circle';
                  }
                },
                methods: {
                  clicked: function(){
                    this.checked = !this.checked;
                  }
                }
              })

对应的模板:

<script type="text/x-template" id="checkbox-item-template">
  <span @click="clicked">
    <span :class="'far ' + prefix"></span>
    {{ text }}
  </span>
</script>

“应用程序”的标记:

<div id="app">
  <checkbox-item 
             v-for="item in items" 
             class="todoItem" 
             :key="item.id"
             v-bind:text="item.title"
             v-bind:checked="item.complete">
  </checkbox-item>
  <br />
  <span>Items unchecked: {{ remaining }}</span>
</div>

初始化代码:

var mdl = new vmToDo();
mdl.items.push(new vmToDoItem(1, 'test 1'));
mdl.items.push(new vmToDoItem(2, 'test 2'));

var vm = new Vue({
  el: '#app',
  data: mdl,
  computed: {
    remaining: function(){
      let currentFilter = this.$data.filters[this.$data.currentFilter];
      return currentFilter().length;
    }
  }
});

一切都很好,单击其中一个项目可以正确切换复选框,但是 mdl.items 中的基础项目没有更新,我在控制台中收到上述警告。

我怀疑双向绑定的工作方式与我预期的不同,我预计它会像 Knockout 一样运行,但我不知道如何按照我预期的方式实现此功能。

为简洁起见,这里是我的问题的完整片段:

function vmToDoItem(id, title){
  this.id = id;
  this.title = title;
  this.complete = false;
} 

function vmToDo(items){
  var self = this;
  
  self.currentFilter = 'all';
  self.items = items || [];
  
  self.filters = {
    all: function(){
      return self.items;
    },
    active: function(){
      return self.items.filter((item) => !item.complete);
    },
    complete: function(){
      return self.items.filter((item) => item.complete);
    }
  }
}

Vue.component('checkbox-item',
             {
                props: ['text', 'checked'],
                template: '#checkbox-item-template',
                computed: {
                  prefix: function(){
                    return !this.checked ? 'fa-circle' : 'fa-check-circle';
                  }
                },
                methods: {
                  clicked: function(){
                    this.checked = !this.checked;
                  }
                }
              })

var mdl = new vmToDo();
mdl.items.push(new vmToDoItem(1, 'test 1'));
mdl.items.push(new vmToDoItem(2, 'test 2'));

var vm = new Vue({
  el: '#app',
  data: mdl,
  computed: {
    remaining: function(){
      let currentFilter = this.$data.filters[this.$data.currentFilter];
      return currentFilter().length;
    }
  }
});
#app span.todoItem{
  display: inline-block;
  cursor: pointer;
  padding-right: 1em;
}

#app span.todoItem span.far {
  vertical-align: middle;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="//use.fontawesome.com/releases/v5.6.3/css/regular.css" />
<link rel="stylesheet" href="//use.fontawesome.com/releases/v5.6.3/css/fontawesome.css" />

<div id="app">
  <checkbox-item 
             v-for="item in items" 
             class="todoItem" 
             :key="item.id"
             v-bind:text="item.title"
             v-bind:checked="item.complete">
  </checkbox-item>
  <br />
  <span>Items unchecked: {{ remaining }}</span>
</div>

<script type="text/x-template" id="checkbox-item-template">
  <span @click="clicked">
    <span :class="'far ' + prefix"></span>
    {{ text }}
  </span>
</script>

标签: javascriptvue.js

解决方案


“道具向下,事件向上”是要记住的规则。数据通过 props 从父级传递给子级。事件由孩子发出并由父母处理(事件向上不是一个严格的规则,但它是变化应该向上树的唯一方式)。在这种情况下,与其尝试为您的 prop 分配值,不如checked发出一个事件并在组件上放置一个处理程序,以便父级将捕获该事件并采取相应的行动:

  <checkbox-item v-for="item in items" class="todoItem" :key="item.id" :text="item.title" :checked="item.complete" @click="item.complete = !item.complete">
  </checkbox-item>

我修改了您的代码段以这种方式工作,并列出了 item.complete 的值,以便您可以看到正在发生的数据更新。

function vmToDoItem(id, title) {
  this.id = id;
  this.title = title;
  this.complete = false;
}

function vmToDo(items) {
  var self = this;

  self.currentFilter = 'all';
  self.items = items || [];

  self.filters = {
    all: function() {
      return self.items;
    },
    active: function() {
      return self.items.filter((item) => !item.complete);
    },
    complete: function() {
      return self.items.filter((item) => item.complete);
    }
  }
}

Vue.component('checkbox-item', {
  props: ['text', 'checked'],
  template: '#checkbox-item-template',
  computed: {
    prefix: function() {
      return !this.checked ? 'fa-circle' : 'fa-check-circle';
    }
  },
  methods: {
    clicked: function() {
      this.$emit('click');
    }
  }
})

var mdl = new vmToDo();
mdl.items.push(new vmToDoItem(1, 'test 1'));
mdl.items.push(new vmToDoItem(2, 'test 2'));

var vm = new Vue({
  el: '#app',
  data: mdl,
  computed: {
    remaining: function() {
      let currentFilter = this.$data.filters[this.$data.currentFilter];
      return currentFilter().length;
    }
  }
});
#app span.todoItem {
  display: inline-block;
  cursor: pointer;
  padding-right: 1em;
}

#app span.todoItem span.far {
  vertical-align: middle;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="//use.fontawesome.com/releases/v5.6.3/css/regular.css" />
<link rel="stylesheet" href="//use.fontawesome.com/releases/v5.6.3/css/fontawesome.css" />

<div id="app">
  <checkbox-item v-for="item in items" class="todoItem" :key="item.id" :text="item.title" :checked="item.complete" @click="item.complete = !item.complete">
  </checkbox-item>
  <br />
  <span>Items unchecked: {{ remaining }}</span>
  <div v-for="item in items">
    {{item.complete}}
  </div>
</div>

<script type="text/x-template" id="checkbox-item-template">
  <span @click="clicked">
    <span :class="'far ' + prefix"></span> {{ text }}
  </span>
</script>


推荐阅读