javascriptvue.jsvuejs2vue-componentvuejs-slots

Pass agruments/data into the methods for displaying the name in new created component in VueJS


I created functionality for the collapse where inside the body table, and there is the "+" add button, when I click on it it adds new collapse(another) with content and the I want to display the name of data row for example "Iphone" and appears new collapse with "Iphone" title/name.
It works great but I have issue, when I add, the title of created collapse is: "object MouseEvent"enter image description here

CollapseSection.vue

<template>
  <div class="accordion" role="tablist">
    <b-button block v-b-toggle.accordion-1 class="collapse-btn" align-h="between">
      {{ selectText }}
      <b-icon :icon="visible ? 'caret-down' : 'caret-up'" class="icon"></b-icon>
    </b-button>
    <b-card no-body class="mb-1">
      <b-collapse id="accordion-1" v-model="visible" accordion="my-accordion" role="tabpanel">
        <SearchField></SearchField>
        <b-card-body>
          <!--     CONTENT WOULD APPEAR INSIDE THIS SLOT      -->
          <slot name="content" :addItem="addItem">

          </slot>
        </b-card-body>
      </b-collapse>
    </b-card>

    <!--  DYNAMIC CONTENT COLLAPSE WHEN CLICK ON ADD BUTTON  -->
    <div v-for="(item, index) in items" :key="index">
      <b-button block v-b-toggle="'accordion-' + (index + 2)" class="collapse-btn">
        {{ item.name }}
        <b-icon :icon="visible ? 'caret-down' : 'caret-up'" class="icon"></b-icon>
      </b-button>
      <b-card no-body class="mb-1">
        <b-collapse :id="'accordion-' + (index + 2)" accordion="my-accordion" role="tabpanel">
          <b-card-body>
            <!--     CONTENT WOULD APPEAR INSIDE THIS SLOT      -->
            <slot name="createdContent">
            </slot>
          </b-card-body>
        </b-collapse>
      </b-card>
    </div>
  </div>
</template>

<script>
import SearchField from "@/components/UI/SearchField.vue";
export default {
  name: "CollapseButton",
  components: {SearchField},
  props: {
    selectText: {
      type: String,
      default: () => "Select",
    },
  },
  data() {
    return {
      isConfiguring: false,
      configuringItem: null,
      items: [],
      visible: false,
    }
  },
  methods: {
    addItem(name) {
      const item = { name };
      this.items.push(item);
      this.isConfiguring = true;
      this.configuringItem = item;
      this.$emit('item-added', item);
    }
  },
}
</script>

DataTable.vue

<template>
<tbody>
<tr v-for="(item, itemIndex) in data" :key="itemIndex">
  <td>
      <slot></slot>
  </td>
  <td v-for="(label, labelIndex) in labels" :key="labelIndex">
    {{ item[label.field] }}
  </td>
</tr>
</tbody
</template>
<script>
export default {
  name: "DataTable",
  components: {ActionColumn},
  props: {
    labels: {
      type: Array,
      required: true,
    },
    data: {
      type: Array,
      required: true,
    },
  },
  methods: {
    addItem(name) {
      this.$emit('add-item', name);
    }
  }
}
</script>

NewLab.vue

<CollapseSection select-text="Select Images">
    <template #content="{ addItem }">
      <DataTable :labels="labels" :data="data" :showAdd="true">
        <b-icon icon="plus" class="add-btn" @click="addItem(data.name)">
        </b-icon>
      </DataTable>
    </template>
    <template #createdContent>
      <CollapseTabContent>

      </CollapseTabContent>
    </template>
</CollapseSection>
<script>
const labels = [
  {text: "Name", field: 'name'},
  {text: "Description", field: 'description'},
]
const data = [
  {name: 'Windows 1', description: 'Password Cracking'},
  {name: 'Windows 13', description: 'SIEM and MISP machine'},
  {name: 'Windows 15', description: 'AD auditing lab'},
  {name: 'Windows 31', description: 'Threat Hunting and Investigation'},
];

export default {
  name: "NewLab",
  components: {DataTable, CollapseSection},
  data() {
    return {
      labels: labels,
      data: data,
    };
  },
</script>

Solution

  • SOLVED. I tried all possible solutions and nothing helped me. I asked GPT, different QNA forums also telegram channels with the VueJS community. I did not use any other technologies like VueX, etc. What I've done to solve the problem, First of all, I separated the Dynamic content of Collapse into a separate component DynamicCollapse.vue

      <template>
      <!--  DYNAMIC CONTENT COLLAPSE WHEN CLICK ON ADD BUTTON  -->
      <div>
        <div v-for="(collapseItem, index) in dynamicCollapses" :key="index">
          <b-button block
                    v-b-toggle="'accordion-' + (index + 2)"
                    class="collapse-btn d-flex justify-content-between align-items-center"
                    align-h="between"
          >
            {{ collapseItem.name }}
            <b-icon
                :icon="isVisible[index + 1] ? 'caret-down' : 'caret-up'"
                class="icon justify-content"
            ></b-icon>
          </b-button>
          <b-card no-body class="mb-1">
            <b-collapse
                :id="'accordion-' + (index + 2)"
                v-model="isVisible[index + 1]"
                accordion="my-accordion"
                role="tabpanel"
            >
              <b-card-body>
                <slot name="createdContent" :item="collapseItem.content"></slot>
              </b-card-body>
            </b-collapse>
          </b-card>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "DynamicCollapse",
      items: [],
      props: {
        dynamicCollapses: {
          type: Array,
          default: () => []
        },
      },
      data() {
        return {
          isVisible: [],
        };
      },
    }
    </script>
    

    After that, I assumed not to pass the add button as a slot into DataTable

    <!-- A dynamic column for add button -->
        <template #cell(first)="data" v-if="showFirstColumn">
          <b-icon icon="plus" class="add-btn" @click="addItemToDynamicCollapse(data.item.name)"></b-icon>
        </template>
    

    here is the method of DataTable.vue

    methods: {
    addItemToDynamicCollapse(name) {
      this.$emit("add-to-dynamic-collapse", name);
    },
    

    and finally in the parent component

    <CollapseSection select-text="Select Available Cloud Instance" collapse-id="instance">
              <template #content>
                <DataTable :fields="labels" :items="data" :showFirstColumn="true" @add-to-dynamic-collapse="addDynamicCollapse"/>
              </template>
              <template #createdContent>
                <CollapseTabContent></CollapseTabContent>
              </template>
            </CollapseSection>
            <DynamicCollapse :dynamic-collapses="dynamicCollapses">
              <template #createdContent="{ item }">
                <CollapseTabContent :data="item"></CollapseTabContent>
              </template>
            </DynamicCollapse>
    

    and inside the parent component in section pass the data and method(I will refactor it in the future)

    data() {
        return {
          dynamicCollapses: [],
        };
      },
      methods: {
        addDynamicCollapse(name) {
          this.dynamicCollapses.push({
            name: name,
            content: {} // Add your content here
          });
        }
      },
    

    I spent a week solving it by reading and gaining a lot of information about the slots, data, scoped slots, etc. So I think this answer would help you.