Как избавиться от клонирования/дублирования элементов во vue

Создаю spa типа trello. Создает доски. В доске создает списки, их выводит разные с разным id, а вот пункты списка выводит с одинаковым id и они дублируются в каждом списке

router.js

import Vue from 'vue'
    import Vuex from 'vuex'

    Vue.use(Vuex)

    export default new Vuex.Store({
      state: {
        boards: JSON.parse(localStorage.getItem('boards') || '[]'),
        lists: [],
        items: []
        // items: JSON.parse(localStorage.getItem('items') || '[]')
        // lists: JSON.parse(localStorage.getItem('lists') || '[]')
      },
      mutations: {
        addBoard(state, board) {
          state.boards.push(board)
          localStorage.setItem('boards', JSON.stringify(state.boards))
        },
        addList(state, list) {
          state.lists.push(list)
          // localStorage.setItem('lists', JSON.stringify(state.lists))
        },
        createItemListt(state, item) {
          state.items.push(item)
          // localStorage.setItem('items', JSON.stringify(state.items))
        }
      },
      actions: {
        addBoard({commit}, board) {
          commit('addBoard', board)
        },
        addList({commit}, list) {
          commit('addList', list)
        },
        createItemListt({commit}, item) {
          commit('createItemListt', item)
        }
      },
      getters: {
        boards: s => s.boards,
        taskById: s => id => s.boards.find(t => t.id === id),
        lists: d => d.lists,
        items: a => a.items
      },
      modules: {
      }
    })

MyBoard.vue

 <template>
  <div>
    <div class="wrapper">
      <div class="row">
        <h1>{{board.title}}</h1>
        <div class="list " v-for="list in lists" :key="list.idList">
          <div class="list__title">
            <h3>{{list.titleList}}</h3>
          </div>
          <div class="list__card" v-for="item in items" :key="item.idItemList">
            <span class="list__item">{{item.itemList}}</span>
            <a class="btn-floating btn-tiny btn-check" tag="button">
              <i class="material-icons">check</i>
            </a>
          </div>
            <createItemList />
        </div>
         <createList />
      </div>

    </div>
  </div>
</template>

<script>
export default {
  computed: {
    board() {
      return this.$store.getters.taskById(+this.$route.params.id);
    },
    lists() {
      return this.$store.getters.lists;
    },
    items() {
      return this.$store.getters.items;
    }
  },

  components: {
    createList: () => import("../components/createList"),
    createItemList: () => import("../components/createItemList")
  }
};
</script>

<style lang="scss" scoped>

.add-item {
  display: block;
}
.textarea-item {
  display: inline-block;
  width: 93%;
  margin: 0 auto;
}
.add-item__btn {
  display: inline-block;

}
.wrapper {
  flex-wrap: nowrap;
}
a.btn-check {
  background: transparent;
  border-radius: 8px;
  width: 1.5rem;
  height: 1.5rem;
  position: absolute;
  right: 1rem;
  top: 0.3rem;
  text-align: center;
}
a.btn-check i {
  line-height: 0px;
  color: #ccc;
  transition: all 1s ease;
}
a.btn-check:hover {
  background: #ccc;
  transition: all 1s ease;
  color: rgb(50, 231, 81);
}
a.btn-check i:hover {
  color: rgb(50, 231, 81);
}
a.btn-large {
  margin-top: 1rem;
}
.create-list p {
  padding-top: 2rem;
}
input::placeholder {
  color: #3557a0;
}
.create-list {
  border: 2px solid #ccc;
  width: 250px;
  height: 1000px;
  border-radius: 10%;
  margin-top: 3rem;
  text-align: center;
  transition: all 0.3s ease;
  display: inline-block;
}
.create-list:hover {
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
}
.list {
  background: rgb(255, 245, 245);
  border: 2px solid #ccc;
  width: 300px;
  max-height: 1000px;
  border-radius: 10px;
  margin-top: 3rem;
  text-align: center;
  transition: all 0.3s ease;
  display: inline-block;
  margin-right: 4rem;
  position: relative;
}
.list__title h3 {
  text-align: left;
  margin-left: 1rem;
  margin-top: 1rem;
  font-size: 2.02rem;
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.list__card {
  position: relative;
  border: 1px solid #ccc;
  border-radius: 5px;
  display: block;
  margin-left: 0.7rem;
  width: 93%;
  text-align-last: left;
}

span.list__item {
  display: block;
  padding: 0.3rem;
}
h1 {
  font-size: 3rem;
}
</style>

CreateItemList.vue

<template>
  <div>
    <div class="add-item">
      <div class="textarea-item input-field" v-show="isAdding">
        <input
          type="text"
          class="validate"
          id="list-item"
          v-model="itemList"
          v-on:keyup.enter="createItemList"
          autofocus
        />
        <label for="list-item">Enter Item List</label>
      </div>
      <a class="waves-effect waves-light btn" v-show="!isAdding" @click="addCard">
        <i class="material-icons right">add</i>Add Card
      </a>
    </div>
  </div>
</template>

<script>
export default {
  data: () => ({
    isAdding: false,
    itemList: "",
  }),
  methods: {
    addCard() {
      this.isAdding = true;
    },
    createItemList() {
       if (this.itemList == "") {
        return false;
      }
      const item = {
        idItemList: Date.now(),
        itemList: this.itemList
      };

      this.$store.dispatch("createItemListt", item);
      this.itemList = "";
      this.isAdding = false;
    }
  }
};
</script>

CreateList.vue

<template>
  <div>
    <div class="row">
      <div class="new-list" v-show="isCreating">
        <div class="list__title input-field">
          <input
            type="text"
            required
            id="list-title"
            class="none validate"
            tag="button"
            autofocus
            v-model="titleList"
            v-on:keyup.enter="createList"
          />
          <label for="list-title">Enter Title List</label>
        </div>

        <a class="btn-floating transparent btn-close" tag="button" @click="closeList">
          <i class="material-icons">close</i>
        </a>
      </div>
      <div class="create-list z-depth-2" v-show="!isCreating">
        <p>Create list</p>
        <a
          class="btn-floating btn-large waves-effect waves-light deep-purple lighten-2 pulse"
          tag="button"
          @click="addList"
          v-on:keyup.enter="addList"
        >
          <i class="material-icons">add</i>
        </a>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data: () => ({
    isCreating: false,
    titleList: "",
    idList: ""
  }),
  methods: {
    addList() {
      this.isCreating = true;
    },
    closeList() {
      this.isCreating = false;
    },
    createList() {
      if (this.titleList == "") {
        return false;
      }
      const list = {
        idList: Date.now(),
        titleList: this.titleList
      };
      this.$store.dispatch("addList", list);
      this.titleList =  "";
      this.isCreating = false;
      console.log(list.titleList);
    }
  }
};
</script>

<style lang="scss" scoped>
.btn-close {
  position: absolute;
  top: -1rem;
  right: -2rem;
  width: 2rem;
  height: 2rem;
}
.btn-close i {
  line-height: 0px;
  color: #000;
  opacity: 0.5;
}
.btn-close i:hover {
  opacity: 1;
}
input::placeholder {
  color: #3557a0;
}
a.btn-large {
  margin-top: 1rem;
}
.create-list p {
  padding-top: 2rem;
}
.create-list {
  border: 2px solid #ccc;
  width: 150px;
  height: 200px;
  border-radius: 10%;
  margin-top: 3rem;
  text-align: center;
  transition: all 1s ease;
  display: inline-block;
}
.create-list:hover {
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
}
.new-list {
  position: relative;
  background: rgb(255, 245, 245);
  border: 2px solid #ccc;
  width: 250px;
  max-height: 800px;
  border-radius: 10%;
  margin-top: 3rem;
  transition: all 0.3s ease;
  display: inline-block;
  margin-right: 4rem;
  padding: 1rem;
  transition: width 2s;
}
</style>

vue-devtools


Ответы (1 шт):

Автор решения: t0lik

Судя по шаблону у вас два независимых массива и вы проходя внутри одного генерируете каждый раз все элементы второго.

Сделайте функцию, которая выдает из items только то, что касается текущего элемента из lists, раз вы полагаете что они зависят друг от друга. Потом используйте ее примерно так:

<div class="list__card" v-for="item in getItems(list)" :key="item.idItemList">
→ Ссылка