Ioann Sulyma
Ioann Sulyma

Reputation: 31

Display list of items 3 per row and select items on click - Vue.js

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>

Upvotes: 1

Views: 1823

Answers (1)

Amaarockz
Amaarockz

Reputation: 4684

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>

Upvotes: 1

Related Questions