2个阵列中的反应性对象

时间:2019-08-30 07:57:57

标签: javascript vuejs2

我有一个对象数组(数组1),可以将其切换到另一个数组(数组2)。添加后,用户可以选择为每个选项键入一个文本字段。切换工作正常,并且在初始创建时具有反应性。但是,如果我有数组2中已经存在的数据,则该项目不再是反应性的。

我做了一个快速的jsfiddle来演示:事件1和3是反应性的,但事件2不再存在,因为它已经存在于newEvents数组中。无论如何,可以将其与原始事件联系起来吗?

    new Vue({
      el: "#app",
      data: {
        events: [
          { id: 1, text: "Event 1"},
          { id: 2, text: "Event 2"},
          { id: 3, text: "Event 3"}
        ],
        savedEvents: [
        	{ id: 2, text: "Event 2", notes: 'Event Notes'}
        ]
      },
      methods: {
      	toggleEvent: function(event){
          let index = this.savedEvents.findIndex(e => e.id == event.id);
          if (index != -1) {
            this.savedEvents.splice(index, 1);
          } else {
            this.savedEvents.push(event);
          }
        },
        inArray: function(id) {
        	return this.savedEvents.some(obj => obj.id == id);
        }
      }
    })
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.btn {
  display: inline-block;
  padding: 5px;
  border: 1px solid #666;
  border-radius: 3px;
  margin-bottom: 5px;
  cursor: pointer;
}

input[type=text]{
  padding: 5px;
 }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h2>Events:</h2>
  <ol>
    <li v-for="event in events">
      <span class="btn" @click="toggleEvent(event)">
        {{ event.text }}
      </span>
      <input type="text" placeholder="Type your note here..." v-model="event.notes" v-if="inArray(event.id)">
    </li>
  </ol>
  <h2>
    Saved Events:
  </h2>
  <ul>
    <li v-for="event in savedEvents">
      <strong>{{ event.text }}</strong> {{ event.notes }}
    </li>
  </ul>
</div>

3 个答案:

答案 0 :(得分:0)

尝试使用<dependencies>$set来避免失去反应性

https://vuejs.org/v2/api/?#Vue-set

https://vuejs.org/v2/guide/reactivity.html

答案 1 :(得分:0)

这里的问题与反应性无关。

通过单击按钮将事件添加到newEvents时,它使用的是与events中的对象相同的对象。由于每个事件只有一个对象,所以一切正常。

对于事件2 ,您从两个代表同一事件的单独对象开始,一个在events中,另一个在newEvents中。更改一个不会更改另一个。

在不知道选择这些数据结构的动机的情况下,很难说出合适的解决方案是什么,但是下面的示例确保了两个数组在事件2 中都包含相同的对象。

我对原始代码所做的唯一更改是data函数。

    new Vue({
      el: "#app",
      data () {
        const data = {
          events: [
            { id: 1, text: "Event 1"},
            { id: 2, text: "Event 2", notes: 'Event Notes'},
            { id: 3, text: "Event 3"}
          ],
          newEvents: []
        }
        
        data.newEvents.push(data.events[1])
        
        return data
      },
      methods: {
      	toggleEvent: function(event){
          let index = this.newEvents.findIndex(e => e.id == event.id);
          if (index != -1) {
            this.newEvents.splice(index, 1);
          } else {
            this.newEvents.push(event);
          }
        },
        inArray: function(id) {
        	return this.newEvents.some(obj => obj.id == id);
        }
      }
    })
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.btn {
  display: inline-block;
  padding: 5px;
  border: 1px solid #666;
  border-radius: 3px;
  margin-bottom: 5px;
  cursor: pointer;
}

input[type=text]{
  padding: 5px;
 }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h2>Events:</h2>
  <ol>
    <li v-for="event in events">
      <span class="btn" @click="toggleEvent(event)">
        {{ event.text }}
      </span>
      <input type="text" placeholder="Type your note here..." v-model="event.notes" v-if="inArray(event.id)">
    </li>
  </ol>
  <h2>
    New Events:
  </h2>
  <ul>
    <li v-for="event in newEvents">
      <strong>{{ event.text }}</strong> {{ event.notes }}
    </li>
  </ul>
</div>

除了使用两个相同对象的列表之外,还有多种方式可以表示此数据。您可以在对象内使用布尔标志。或者,您可以使用一个单独的对象来保存笔记,并以事件ID为键。很难知道哪种方案最适合您的情况。

更新

基于注释,您可以执行以下操作,以在加载events时将savedEvents中的对象用作规范版本:

loadSavedEvents () {
  // Grab events from the server
  someServerCall().then(savedEvents => {
    // Build a map so that the objects can be grabbed by id
    const eventMap = {}

    for (const event of this.events) {
      eventMap[event.id] = event
    }

    // Build the list of server events using the objects in events
    this.savedEvents = savedEvents.map(savedEvent => {
      const event = eventMap[savedEvent.id]
      this.$set(event, 'notes', savedEvent.notes)
      return event
    })
  })  
}

答案 2 :(得分:0)

如@skirtle所指出,列表数组中的对象需要被推入第二个数组以使其具有反应性。我已经解决了这个问题,方法是循环遍历并匹配id,然后将该对象推入第二个数组。不确定这是否是最好/最有效的方法,但现在可以使用。

new Vue({
  el: "#app",
  data: {
    eventsList: [
      { id: 1, text: "Event 1"},
      { id: 2, text: "Event 2"},
      { id: 3, text: "Event 3"}
    ],
    savedEvents: [
    	{ id: 2, text: "Event 2", notes: 'Event Notes'}
    ]
  },
  mounted() {
		this.init();
  },
  methods: {
    init: function() {
  		let _temp = this.savedEvents;
      this.savedEvents = [];

      _temp.forEach(event => {
        this.eventsList.forEach(x => {
          if (event.id == x.id) {
            this.$set(x, "notes", event.notes);
            this.savedEvents.push(x);
          }
        });
      });
  	},
  	toggleEvent: function(event){
      let index = this.savedEvents.findIndex(e => e.id == event.id);
      if (index != -1) {
        this.savedEvents.splice(index, 1);
      } else {
        this.savedEvents.push(event);
      }
    },
    inArray: function(id) {
    	return this.savedEvents.some(obj => obj.id == id);
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.btn {
  display: inline-block;
  padding: 5px;
  border: 1px solid #666;
  border-radius: 3px;
  margin-bottom: 5px;
  cursor: pointer;
}

input[type=text] {
  padding: 5px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h2>Events:</h2>
  <ul>
    <li v-for="event in eventsList">
      <span class="btn" @click="toggleEvent(event)">
        {{ event.text }}
      </span>
      <input type="text" placeholder="Type your note here..." v-model="event.notes" v-if="inArray(event.id)">
    </li>
  </ul>
  <h2>
    Saved Events:
  </h2>
  <ul>
    <li v-for="event in savedEvents">
      <strong>{{ event.text }}</strong> {{ event.notes }}
    </li>
  </ul>
</div>