How to show an attribute of an item (generated by a loop) on-click?

396 Views Asked by At

In my vue3 app I have three boards :

  • one for draggable elements (already defined in a JSON file)
  • one for droppable elements (stored in a empty list in the vue)
  • one to display the properties of a dropped element, when you click on it

I'm using Vuedraggable for the drag and drop and here's how it looks : IMG

My problem is on the third board : I want to click on an item in the "drop board" and show its properties into the "properties board" knowing that an item have multiple properties. I've tried for days and still can't find the solution, can somebody help me please?

I'm new to Vue and StackOverflow, I've tried to be as clear as possible, sorry if I made mistakes.

Home.vue - first of all, where I declare my boards :

<template>
  <div>
      <Board v-for="(board, index) in boards" :key="index" :id="index" :board="board"/>
  </div>
</div>
</template>

<script>
import Board from "@/components/Board.vue";

export default {
  components: {
    Board,
  },
  data: () => ({
    boards: [
      {
        title: "Toolbox Board",
      },
      {
        title: "Mockup Board",
      },
      { title: "Properties" },
    ],
  }),
};

Board.vue - The "drag board" code:

<div class="dd-container" v-if="board.title == 'Toolbox Board'">
  <draggable v-model="dragItems" item-key="id":group="{ name: 'items', pull: 'clone', put: false }" :clone="cloneItems" @change="log">
    <template #item="{ element }">
      <div class="item">
        {{ element.title }}
      </div>
    </template>
  </draggable>
</div>

Board.vue - The "drop board" code:

<div class="dd-container" v-if="board.title == 'Mockup Board'">
  <draggable
    v-model="dropItems" item-key="id" group="items" @change="log">
    <template #item="{ element }">
      <div class="item">
        {{ element.title }}
      </div>
    </template>
  </draggable>
</div>

Board.vue - The "properties board":

<div class="dd-container" v-if="board.title == 'Properties'">
    {{ property }} ??
</div>

Board.vue - Some of the script :

import dragItemsList from "/dragItems.json";
import draggable from "vuedraggable";

export default {
  components: {
    draggable,
  },

  data() {
    return {
      dragItems: dragItemsList,
      dropItems: [],

DragItems.json - Most importantly the JSON file :

[
  {"title": "Simple list","id": 1,"properties": ["this is a property"],"fixed": true},
  {"title": "Search list","id": 2,"properties": ["this is a property"],"fixed": true},
  {"title": "Simple options","id": 3,"properties": ["this is a property"],"fixed": true},
  {"title": "Multiple options","id": 4,"properties": ["this is a property"],"fixed": true },
  {"title": "Location","id": 5,"properties": ["this is a property"],"fixed": true},
  {"title": "Picture","id": 6,"properties": ["this is a property"],"fixed": true},
  {"title": "Signature","id": 7,"properties": ["this is a property"],"fixed": true},
  {"title": "Audio","id": 8,"properties": ["this is a property"],"fixed": true},
  {"title": "Todo list","id": 9,"properties": ["this is a property"],"fixed": true},
  {"title": "Grouped items","id": 10, "properties": ["this is a property"],"fixed": true},
  {"title": "Divider","id": 11, "properties": ["this is a property"],"fixed": true},
  {"title": "Grouping container","id": 12, "properties": ["this is a property"],"fixed": true},
  {"title": "NFC reader","id": 13, "properties": ["this is a property"],"fixed": true},
  {"title": "QR code scanner","id": 14, "properties": ["this is a property"],"fixed": true},
  {"title": "Barcode scanner","id": 15, "properties": ["this is a property"],"fixed": true},
  {"title": "Fingerprint read (Idemia)","id": 16, "properties": [],"fixed": false}
]
1

There are 1 best solutions below

4
On BEST ANSWER

I think you have put yourself into a corner because you are trying to use v-for where it is not needed, the lists look the same but you can just have them as separate components that happen to use the same classes.

Second problem is that you are overloading the Board.vue component with 3 different implementations. If there are three boards that each behave differently from the others, then split those parts into their own components.

So you would end up with 3 board components, each displaying a different kind of board.

If you insist on avoiding writing things twice you can create a component to display a board and have the contents thereof be a slot.

Going your route, of forcing the templating to use a v-for over two different types of columns is not working out well, because you shouldn't put draggable lists and a properties pane in the same v-for loop.

Because the drag and drop boards both are using draggable, you could make a single Board component that would present a draggable list of items and then emit an event (e.g. drop) when an item is dropped onto the board.

Because the difference in the snippets you shared for those boards was in the configuration of draggable, you can create props for enabling/disabling the drag-and-drop features for those boards (e.g. allow-drag, allow-drop).

<!-- Home.vue -->
<template>
  <div>
    <Board :items="dragItems" @select="select" allow-drag/>
    <Board :items="dropItems" @drop="transfer" @select="item => select(item)" allow-drop />
    <PropertiesBoard :selected="selectedItem" />
  </div>
</template>
<script>
export default {
  data: () => ({
    dragItems: [],
    dropItems: [],
    selectedItem: null,
  }),
  methods: {
    select(item) {
      console.log('Dropped', item);
      this.selectedItem = item;
    },
  },
};

</script>

<!-- Board.vue -->
<template>
  <button @click="testme">Trigger</button>
</template>
<script>
export default {
  methods: {
    testme() {
      const item = {
        id: 1,
        name: 'This is my item',
      };
      this.$emit('drop', item);
    },
  },
};
</script>

In this snippet the @select event is emitted by the boards when an item is selected, so the Home component can then set it as the focussed item and bind it to the PropertiesBoard to display its properties.

Edit: I've updated this snippet to show how to emit events from Board.vue