1

i have two tasks:

  1. Displaying the items of the item list 3 per row
  2. Add a input field used to edit the title field in the currently selected element (selection should be made by clicking).

So, I made the first task based on this solving V-if inside v-for - display list of items in two columns and now i have a problem with my second task selecting method. It should be working for every item but on click selects an items from every list and can to edit only from first list. I think that problem can be in onItemClick(index) method but don't know why. Any ideas about that?

Vue.component('item-list', {
  template: '#item-list-template',
  data() {
    return {
      items: [{
          title: 'item 1'
        },
        {
          title: 'item 2'
        },
        {
          title: 'item 3'
        },
        {
          title: 'item 4'
        },
        {
          title: 'item 5'
        },
        {
          title: 'item 6'
        }
      ],
      activeIndex: -1,
      rowArray: []
    }
  },
  mounted(){
      this.fakeFetchData();
  },
  methods: {
    // new method from example solving
    fakeFetchData(){
        var cloned = this.items.slice();
        while (cloned.length > 0) {
            let chunk = cloned.splice(0,3);
            this.rowArray.push(chunk);
        }  
    },
    onItemClick(index) {
      this.activeIndex = this.activeIndex === index ? -1 : index;
    },
    setActiveItemValue(event) {
      const foundItem = this.items[this.activeIndex];
      if (!foundItem) return;
      
      return this.items[this.activeIndex].title = event.currentTarget.value;
    }
  },
  computed: {
    activeItemValue() {
      return this.items[this.activeIndex]?.title ?? '';
    }
  }
});

Vue.component('item', {
  template: '#item-template',
  props: {
    isActive: Boolean,
    title: String
  }
});

new Vue({
  el: '#app'
});
li.active {
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <item-list></item-list>
</div>

<script type="text/x-template" id="item-list-template">
  <div>
    <input type="text" placeholder="Edit selected items" :value="activeItemValue" @input="setActiveItemValue" />
    <div class="items-col">
      <ul class="items-list" v-for="(row, rowIndex) in rowArray" :key="rowIndex">
        <item v-for="(item, i) in row" :key="i" :title="item.title" :isActive="activeIndex === i" @click.native="onItemClick(i)" />
      </ul>
    </div>
  </div>
</script>

<script type="text/x-template" id="item-template">
  <li class="item" :class="{ active: isActive }">{{ title }}</li>
</script>
<style>
    .items-list {
        display: flex;
    }
</style>
Ioann Sulyma
  • 31
  • 1
  • 6

1 Answers1

1

I have modified and moved the fakeFetchData() from mounted to inside computed and modified the inner v-for inside the template. Check it out

Vue.component('item-list', {
  template: '#item-list-template',
  data() {
    return {
      items: [{
          title: 'item 1'
        },
        {
          title: 'item 2'
        },
        {
          title: 'item 3'
        },
        {
          title: 'item 4'
        },
        {
          title: 'item 5'
        },
        {
          title: 'item 6'
        }
      ],
      activeIndex: -1,
      rowArray: []
    }
  },
  methods: {
    // new method from example solving
    onItemClick(index) {
      this.activeIndex = this.activeIndex === index ? -1 : index;
    },
    setActiveItemValue(event) {
      const foundItem = this.items[this.activeIndex];
      if (!foundItem) return;
      
      return this.items[this.activeIndex].title = event.currentTarget.value;
    }
  },
  computed: {
    activeItemValue() {
      return this.items[this.activeIndex]?.title ?? '';
    },
    fakeFetchData(){

        // ********* Changes done below ************

        var cloned = this.items.map((item, index) => { 
           return {title: item.title, id: index}
        });
        this.rowArray = [];
        while (cloned.length > 0) {
            let chunk = cloned.splice(0,3);
            this.rowArray.push(chunk);
        }  
        return this.rowArray;

        // ******* End of changes ********
    },
  }
});

Vue.component('item', {
  template: '#item-template',
  props: {
    isActive: Boolean,
    title: String
  }
});

new Vue({
  el: '#app'
});
li.active {
  background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <item-list></item-list>
</div>

<script type="text/x-template" id="item-list-template">
  <div>
    <input type="text" placeholder="Edit selected items" :value="activeItemValue" @input="setActiveItemValue" />
    <div class="items-col">
      <ul class="items-list" v-for="(row, rowIndex) in fakeFetchData" :key="rowIndex">
        <!-- Changes done --><item v-for="item in row" :key="item.id" :title="item.title" :isActive="activeIndex === item.id" @click.native="onItemClick(item.id)" />
      </ul>
    </div>
  </div>
</script>

<script type="text/x-template" id="item-template">
  <li class="item" :class="{ active: isActive }">{{ title }}</li>
</script>
<style>
    .items-list {
        display: flex;
    }
</style>
Amaarockz
  • 4,348
  • 2
  • 9
  • 27
  • Thank you very much, it works perfectly! – Ioann Sulyma Nov 16 '21 at 16:23
  • @IoannSulyma Great! If it works for you and you are happy - then in general you would accept the answer. This will not only help other people with the same issue but it will also mean people are more likely to help you in the future with any other issues you have. You can read about accepting here: https://stackoverflow.com/help/accepted-answer – Amaarockz Nov 16 '21 at 16:27