【问题标题】:Reactive object in 2 arrays2个数组中的反应对象
【发布时间】:2019-08-30 07:57:57
【问题描述】:

我有一个对象数组(数组 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>

【问题讨论】:

    标签: javascript vuejs2


    【解决方案1】:

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

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

    在 事件 2 的情况下,您从代表同一事件的两个独立对象开始,一个在 events 中,另一个在 newEvents 中。改变一个不会改变另一个。

    如果不知道您选择这些数据结构的动机,很难说出合适的解决方案是什么,但下面的示例确保两个数组都包含相同的 Event 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 键入的笔记。很难知道哪种方案最适合您的方案。

    更新:

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

    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
        })
      })  
    }
    

    【讨论】:

    • 感谢您的回复。我理解你的意思,它不是第二个数组中的同一个对象。我认为我需要做的是遍历第一个数组并通过 id 找到第二个数组中存在的任何匹配项,然后将在第一个数组中找到的对象推回第二个数组并设置 notes 属性?你有一个干净的 es6 方法吗?
    • 我已将数组更新为 saveEvents,因为这可能有助于更有意义。这是一个更大的 UI 的一部分,但基本上用户可以切换以从预定义的列表中保存他们想要的事件。这会保存到数据库中,当他们第一次创建条目时这很好,但是当他们想要编辑条目并填充他们最初添加的事件时就会出现问题。
    • @Pedro 我已经用一个示例更新了我的答案,说明如何在没有嵌套循环的情况下执行此操作。但是,除非两个数组的大小非常大,否则您不会注意到这样做有任何显着的性能提升。
    • 我会接受你的回答,因为它比嵌套循环更干净
    【解决方案2】:

    尝试使用$set 和$delete 以避免失去反应性

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

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

    【讨论】:

    • 为什么?这两个人是做什么的?我该如何使用它们?请在您的答案中添加一些解释,并举例说明这对 OP 有何帮助。甚至可能是指向我可以阅读这两者的文档的链接。
    【解决方案3】:

    正如@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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 2023-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-23
      相关资源
      最近更新 更多