代码之家  ›  专栏  ›  技术社区  ›  Adam H

Vuejs-查看模型、子级和更新值

  •  1
  • Adam H  · 技术社区  · 6 年前

    我正在尝试学习Vuejs,并且遇到了一个非常基本的问题。我有很多击倒和胡子的经验,我认为这可能是导致我问题的原因。

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

    避免直接改变属性,因为值将被覆盖 每当父组件重新呈现时。而是使用数据或 基于属性值的计算属性。正在变异的道具: “核对”

    这就是我想要做的;

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

    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>
    

    “app”的标记:

    <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中的基础项没有更新,我在控制台中收到上述警告。

    我怀疑双向绑定的工作方式与我预期的不同,我预期它的工作方式会像淘汰一样,我对如何使这个函数像我预期的那样工作感到困惑。

    为了简洁起见,以下是我的问题的完整片段:

    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>
    1 回复  |  直到 6 年前
        1
  •  1
  •   Roy J    6 年前

    “支撑下降,事件上升”是要记住的规则。数据通过props从父级传递到子级。事件由子级发出并由父级处理(事件向上不是一个严格的规则,而是更改应该向上树的唯一方式)。在这种情况下,不要试图为属性赋值 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>