accordion-item not picking up v-for directive variable

This sample component below displays "foo" in both accordion items.

Can the code be fixed to properly display "foo" & "bar", without moving the accordion construction to javascript?

Related question: how can I specify the data to pass to the item's content template (#content="{}") - again without resorting to the JS APIs?

Thanks!

<template>
<ejs-accordion>
<e-accordionitems>
<template v-for="(name) in names">
<e-accordionitem content="content" header="header">
<template #content="{}">
Names: {{ name }}
</template>
</e-accordionitem>
</template>
</e-accordionitems>
</ejs-accordion>
</template>

<script lang="ts" setup>
import {
AccordionComponent as EjsAccordion, AccordionItemsDirective as EAccordionitems, AccordionItemDirective as EAccordionitem
} from "@syncfusion/ej2-vue-navigations";

const names = [ "foo", "bar"]
</script>

7 Replies

RR Ram Raju Elaiyaperumal Syncfusion Team January 30, 2024 03:14 PM UTC

Hi Franck Mangin,

Thank you for reaching out. We've reviewed your code and found a way to achieve your desired outcome. The issue was related to how the content for each accordion item was being set.


In Vue, you can bind data to component properties using the v-bind directive (or its shorthand :). In your case, you can bind the name variable to the content and header properties of the e-accordionitem. Here's the corrected code:


<template>

  <ejs-accordion>

    <e-accordionitems>

      <e-accordionitem v-for="(name, index) in names" :key="index" :header="'Names: ' + name" :content="name">

      </e-accordionitem>

    </e-accordionitems>

  </ejs-accordion>

</template>

 

<style>

@import ../node_modules/@syncfusion/ej2-navigations/styles/material.css;

@import ../node_modules/@syncfusion/ej2-buttons/styles/material.css;

@import ../node_modules/@syncfusion/ej2-base/styles/material.css;

</style>

 

<script  setup>

import {

  AccordionComponent as EjsAccordion,

  AccordionItemsDirective as EAccordionitems,

  AccordionItemDirective as EAccordionitem,

  Accordion,

} from "@syncfusion/ej2-vue-navigations";

 

const names = [ "foo", "bar"]

</script>


In this code, the :content attribute is bound to the name variable, which sets the content of each accordion item to the corresponding name. The :header attribute is also bound to a dynamic string that includes the name variable, which sets the header of each accordion item to "Names: " followed by the name.


We hope this helps! If you have any other questions, please don't hesitate to ask.



Regards,

Ram



FM Franck Mangin January 30, 2024 03:45 PM UTC

Hi Ram,

Thanks for yoru suggestion but that doesn't help. I need to use a complex embedded template as the accordion item, and I don't want to define it in javascript.

In the solution you proposed for example the items will display "foo" and "bar" instead of "Names: foo" and "Names: bar"... and this was a simplified example, my actual code uses an external component:


<template v-for="(slot) in slots">

<e-accordionitem content="content" header="header">

<template #content="{}">
<SlotSchedule v-if="slot.useSessions" :slotId="slot.id"/>           <SlotCalendar v-else :slotId="slot.id"/>
</template>
</e-accordionitem>

...


In other terms I want to use a named slot to define the item content, how can I pass data to that slot?

The approach is described here on your blog: https://www.syncfusion.com/blogs/post/introducing-slot-template-support-for-syncfusion-vue-components.aspx/amp

Franck



RR Ram Raju Elaiyaperumal Syncfusion Team January 31, 2024 11:52 AM UTC

Hi Franck Mangin,

In response to your query, you can use Vue's dynamic slot names and conditional rendering to customize the content of each accordion item. Here's a code snippet demonstrating this:

<template>

  <ejs-accordion>

    <e-accordionitems>

      <e-accordionitem v-for="(slot, index) in slots" :key="index" :header="'Slot: ' + slot.id" :content="getContent(slot, index)">

        <template v-if="getContent(slot, index)===`scheduleTemplate`" v-slot:scheduleTemplate>

          <SlotSchedule :slotId="slot.id"/>

        </template>

        <template v-if="getContent(slot, index)===`calendarTemplate`" v-slot:calendarTemplate>

          <SlotCalendar :slotId="slot.id"/>

        </template>

      </e-accordionitem>

    </e-accordionitems>

  </ejs-accordion>

</template>


The getContent function determines the slot name based on the useSessions property of each slot. We then conditionally render the templates for 'scheduleTemplate' and 'calendarTemplate' based on this slot name.


This approach allows you to use a complex embedded template for the accordion item and pass data to it.


Please check the attached sample for a complete implementation of this solution.


Regards,

Ram


Attachment: VueCompositionEditorTemplate_(2)_241a7272.zip


FM Franck Mangin January 31, 2024 01:06 PM UTC

Thanks, unfortunately this doesn't work, you are back to my initial issue: slot.id is always "1" inside the named slot.

The :content binding does see the right slot, but the named v-slots do not.

          <SlotSchedule :slotId="slot.id"/>




I need access to the (slot, index) data bound by the v-for


Here is a complete example that show the issue:

<template>
<ejs-accordion>
<e-accordionitems>
<e-accordionitem v-for="(slot, index) in slots" :key="index" :content="getContent(slot)"
:header="'Slot: ' + slot.id">
<template v-slot:scheduleTemplate="{}">
Slot ID: {{ slot.id }}
</template>
<template v-slot:calendarTemplate="{}">
Slot ID: {{ slot.id }}
</template>
</e-accordionitem>
</e-accordionitems>
</ejs-accordion>
</template>

<script lang="ts" setup>
import {
AccordionComponent as EjsAccordion,
AccordionItemDirective as EAccordionitem,
AccordionItemsDirective as EAccordionitems
} from "@syncfusion/ej2-vue-navigations";

const slots = [{id: 1, useSessions: true}, {id: 2, useSessions: false}];

const getContent = (slot: any) => slot?.useSessions ? 'scheduleTemplate' : 'calendarTemplate';

</script>


RR Ram Raju Elaiyaperumal Syncfusion Team February 1, 2024 01:51 PM UTC

Hi Franck Mangin,

Based on your query, it appears that you're having trouble accessing the correct slot.id within your named slots in Vue. This issue arises due to the same template being used for both accordion items.


To resolve this, you can add a v-if check before rendering the template. This will ensure that the correct template is used for each accordion item based on the getContent(slot) function. Here's how you can modify your code:
 

<template>

  <ejs-accordion>

    <e-accordionitems>

      <e-accordionitem v-for="(slot, index) in slots" :key="index" :content="getContent(slot)"

                       :header="'Slot: ' + slot.id">

        <template v-if="getContent(slot, index)===`scheduleTemplate`" v-slot:scheduleTemplate="{}">

          Slot ID: {{ slot.id }}

        </template>

        <template  v-if="getContent(slot, index)===`calendarTemplate`" v-slot:calendarTemplate="{}">

          Slot ID: {{ slot.id }}

        </template>

      </e-accordionitem>

    </e-accordionitems>

  </ejs-accordion>

</template>


In the above code, the v-if directive checks whether the getContent(slot) function returns 'scheduleTemplate' or  'calendarTemplate'. Depending on the result, the corresponding template is rendered, ensuring that the correct slot.id is accessed.


Regards,

Ram



FM Franck Mangin February 2, 2024 11:38 AM UTC

Please do check the value of slot.id in this named slot:

<template v-if="getContent(slot, index)===`scheduleTemplate`" v-slot:scheduleTemplate="{}">

   Slot ID: {{ slot.id }}

</template>


... you will find that it is not correct.


I think this is a feature request in the end, what would make this work is something like this: a new itemDataSource property on the accordionitem, that the directive would provide to named slots.

Today it provies the root accordion datasource as data, which is useless since the item doesn't know what element to pick in the array.


<template>
  <ejs-accordion>
    <e-accordionitems>
      <e-accordionitem v-for="(slot, index) in slots" :key="index" :content="content" :item-data-source="slot">
        <template #content="mySlot">
          Slot ID: {{ mySlot.id }}
        </template>
      </e-accordionitem>
    </e-accordionitems>
  </ejs-accordion>
</template>


<script lang="ts" setup>
import {
  AccordionComponent as EjsAccordion,
  AccordionItemDirective as EAccordionitem,
  AccordionItemsDirective as EAccordionitems
} from "@syncfusion/ej2-vue-navigations";


const slots = [{id: 1, useSessions: true}, {id: 2, useSessions: false}];


const getContent = (slot: any) => slot?.useSessions ? 'scheduleTemplate' : 'calendarTemplate';


</script>




RR Ram Raju Elaiyaperumal Syncfusion Team February 8, 2024 11:47 AM UTC

Hi Franck Mangin,

We observed that in the provided sample, the template was not dynamically rendered, and the initial value was set to the templates. To address this issue, we have provided a corrected code snippet that utilizes the v-for directive to dynamically render the templates based on the values in the slots array. The :content property is appropriately set to generate unique content slots for each accordion item, ensuring that the named slot in the template correctly references the slot.id value.


Please find the corrected code below:

<template>

  <ejs-accordion>

    <e-accordionitems>

      <e-accordionitem v-for="(slot, index) in slots" :key="index" :content="`content${slot.id}`"  :header="'Slot: ' + slot.id" :item-data-source="slot">

        <template #[`content${slot.id}`]>

          Slot ID: {{ slot.id }}   

        </template>

      </e-accordionitem>

    </e-accordionitems>

  </ejs-accordion>

</template>

<script lang="ts" setup>

import {

  AccordionComponent as EjsAccordion,

  AccordionItemDirective as EAccordionitem,

  AccordionItemsDirective as EAccordionitems

} from "@syncfusion/ej2-vue-navigations";

const slots = [{id: 1, useSessions: true}, {id: 2, useSessions: false}];

const getContent = (slot: any) => slot?.useSessions ? 'scheduleTemplate' : 'calendarTemplate';

</script>


We have attached the runnable sample for your reference.



Regards,

Ram


Loader.
Up arrow icon