Vue 3 Composition API gantt Chart Custom Template Not Working

Hello, I'm currently working on a project to refactor a project's framework from vue 2 to vue 3 composition API, I was able to convert most of it; however 2 specific parameters breaks my gantt chart: labelSettings & taskbarTemplate.


As far as I can tell, the custom template worked in vue 2 gantt chart, and I believe I'm importing the template correctly since also using the parameters milestoneTemplate ​and thats working fine. Some help would be very much appreciated!

Error I'm getting:

ERROR
Cannot read properties of undefined (reading 'components')

TypeError: Cannot read properties of undefined (reading 'components')
    at eval (webpack-internal:///./node_modules/@syncfusion/ej2-vue-base/src/template.js:78:64)
    at ChartRows.eval [as leftTaskLabelTemplateFunction] (webpack-internal:///./node_modules/@syncfusion/ej2-base/src/template-engine.js:45:22)
    at ChartRows.getLeftLabelNode (webpack-internal:///./node_modules/@syncfusion/ej2-gantt/src/gantt/renderer/chart-rows.js:699:42)
    at ChartRows.getGanttChartRow (webpack-internal:///./node_modules/@syncfusion/ej2-gantt/src/gantt/renderer/chart-rows.js:1396:34)
    at ChartRows.createTaskbarTemplate (webpack-internal:///./node_modules/@syncfusion/ej2-gantt/src/gantt/renderer/chart-rows.js:1354:33)
    at ChartRows.renderChartRows (webpack-internal:///./node_modules/@syncfusion/ej2-gantt/src/gantt/renderer/chart-rows.js:113:14)
    at GanttChart.renderChartElements (webpack-internal:///./node_modules/@syncfusion/ej2-gantt/src/gantt/base/gantt-chart.js:98:41)
    at Observer.notify (webpack-internal:///./node_modules/@syncfusion/ej2-base/src/observer.js:106:29)
    at Component.notify (webpack-internal:///./node_modules/@syncfusion/ej2-base/src/component.js:301:32)
    at Gantt.treeDataBound (webpack-internal:///./node_modules/@syncfusion/ej2-gantt/src/gantt/base/gantt.js:1300:14)

Code Snippet:

````

<template>
   <v-container>
      ... removing all other code...
      <ejs-gantt
            id="GanttContainer"
            ref="gantt"
            :dataSource="data"
            :taskFields="taskFields"
            :columns="columns"
            :toolbar="toolbar"
            :toolbar-click="toolbarClick"
            :allow-excel-export="true"
            :allow-pdf-export="true"
            :editSettings="editSettings"
            :row-height="rowHeight"
            :taskbar-height="taskbarHeight"
            :splitter-settings="splitterSettings"
            :allow-unscheduled-tasks="allowUnscheduledTasks"
            :include-weekend="true"
            :timeline-settings="timelineSettings"
            :duration-unit="durationUnit"
            :date-format="dateFormat"
            :day-working-time="dayWorkingTime"
            :work-week="workWeek"
            :highlight-weekends="true"
            :allowSorting="true"
            :sortSettings="sortSettings"
            :allow-filtering="true"
            :load="ganttloaded"
            :created="ganttcreated"
            :record-double-click="recordDoubleClick"
            :data-bound="dataBound"
            :milestoneTemplate="milestoneTemplate" <---- This works fine
            :labelSettings="labelSettings" <---- This will break
​ :taskbarTemplate="taskbarTemplate" <---- This will break
          />
        ...
  v-container>
template>
<script>
import {
  GanttComponent,
  Edit,
  Selection,
  Toolbar,
  DayMarkers,
  Sort,
  Filter,
  ExcelExport,
  PdfExport,
} from"@syncfusion/ej2-vue-gantt";
...
importMilestoneTemplatefrom"./taskbar-milestone-temp.vue";
importChildTemplatefrom"./taskbar-temp.vue";
importLeftLabelTemplatefrom"./tasklabel-left-temp.vue";
importRightLabelTemplatefrom"./tasklabel-right-temp.vue";
...
exportdefault {
   ...
  setup(props) {
    provide(
    'gantt', [
      Edit,
      Selection,
      Toolbar,
      DayMarkers,
      Sort,
      Filter,
      ExcelExport,
      PdfExport,
    ]);
   const labelSettings =({
      leftLabel: () => ({ template: LeftLabelTemplate }),
      rightLabel: () => ({ template: RightLabelTemplate }),
    });
    const taskbarTemplate = () => ({ template: ChildTemplate });
    const milestoneTemplate = () => ({ template: MilestoneTemplate });
    ...
    return {
      ....
      labelSettings,
      taskbarTemplate,
      milestoneTemplate,
      ...
    }
}

...


8 Replies

UA Udhayakumar Anand Syncfusion Team September 1, 2023 07:24 AM UTC

Jun Kim


Greetings from Syncfusion


Upon reviewing your query, we have identified certain issues during our code analysis. Our recommendation is to employ the template within the `app.vue` file using the `v-slot` of Vue, rather than importing it from and exporting it in a separate file. This approach is more efficient. Additionally, we've observed that there is no space in the import statement. You can find the modified sample link and code snippet below


Code Snippet:

/*App.vue*/

<template v-slot:leftLabelTemplate="{data}">

          <div>

            {{data.TaskName}}

          </div>

        </template>

        <template v-slot:rightLabelTemplate="{data}">

          <div>

            {{data.TaskName}}

          </div>

        </template>

 


Sample Link : https://www.syncfusion.com/downloads/support/directtrac/general/ze/vite-project-732076731


Demo Sample : https://ej2.syncfusion.com/vue/demos/#/bootstrap5/gantt/tasklabel-template.html


UG LINK : https://ej2.syncfusion.com/vue/documentation/gantt/appearance-customization#taskbar-template


If you face any issue while using v-slot please get back to us with the details


Regards,

Udhayakumar



JK Jun Kim replied to Udhayakumar Anand September 1, 2023 03:49 PM UTC

Hello, thanks for the reply,


unfortunately, I'm still getting issue with this method using v-slot.

So I tried coping the method using the demo link into my code


<template>
  <v-container>
... 
     <ejs-gantt
            id="GanttContainer"
            ref="gantt"
            :dataSource="data"
            :taskFields="taskFields"
            :columns="columns"
            :toolbar="toolbar"
            :toolbar-click="toolbarClick"
            :allow-excel-export="true"
            :allow-pdf-export="true"
            :editSettings="editSettings"
            :row-height="rowHeight"
            :taskbar-height="taskbarHeight"
            :splitter-settings="splitterSettings"
            :allow-unscheduled-tasks="allowUnscheduledTasks"
            :include-weekend="true"
            :timeline-settings="timelineSettings"
            :duration-unit="durationUnit"
            :date-format="dateFormat"
            :day-working-time="dayWorkingTime"
            :work-week="workWeek"
            :highlight-weekends="true"
            :allowSorting="true"
            :sortSettings="sortSettings"
            :allow-filtering="true"
            :load="ganttloaded"
            :created="ganttcreated"
            :record-double-click="recordDoubleClick"
            :data-bound="dataBound"
            :milestoneTemplate="milestoneTemplate"
            :labelSettings="labelSettings"                        <--- still issue
          >
          <template v-slot:leftLabelTemplate="{data}">
            <div>
              <template>
                <div>
                  {{ data.taskData }}
                </div>
              </template>
            </div>
          </template>


          <template v-slot:rightLabelTemplate="{data}">
            <div>
              <template>
                <div>
                  {{ data.taskData }}
                </div>
              </template>
            </div>
          </template>
          </ejs-gantt>
  ...
  </v-container>
</template>
<script>
import {
  GanttComponent,
  Edit,
  Selection,
  Toolbar,
  DayMarkers,
  Sort,
  Filter,
  ExcelExport,
  PdfExport,
} from"@syncfusion/ej2-vue-gantt";
...
import MilestoneTemplate from"./taskbar-milestone-temp.vue";
...
exportdefault {
   ...
  setup(props) {
    provide(
    'gantt', [
      Edit,
      Selection,
      Toolbar,
      DayMarkers,
      Sort,
      Filter,
      ExcelExport,
      PdfExport,
    ]);
    ...
 const labelSettings =({
      leftLabel: () => ({ template: "LeftLabelTemplate" }),
      rightLabel: () => ({ template: "RightLabelTemplate" }),
    });
const milestoneTemplate = () => ({ template: MilestoneTemplate });
...
return {
      ....
      labelSettings,
      milestoneTemplate,
      ...
    }
}


I still getting the same error unfortunately
"Cannot read properties of undefined (reading 'components')"


If I tried to copy exactly like what is done in the script, it loads but incorrectly (Only loads in the string instead of the template) 

const labelSettings =({
      leftLabel: "LeftLabelTemplate",
      rightLabel: "RightLabelTemplate",
    });
Image_9633_1693582660789

The Demo sample you provide was done in Vue 3 Option API, is it possible to get that example in Vue 3 composition API?


Thank you



LA Lokesh Arjunan Syncfusion Team September 2, 2023 03:54 AM UTC

Hi Jun Kim


We are validating your query and we will provide you sample within two business days(Sep 5).


Until then we appreciate your patience.


Regards,

Lokesh



UA Udhayakumar Anand Syncfusion Team September 5, 2023 01:20 PM UTC

Jun Kim


We have thoroughly examined your inquiry and have configured the sample utilizing the Composition API. Regrettably, we have been unable to reproduce the issue that you have reported. To facilitate further validation, could you kindly furnish us with the following information?


  1. Confirm that you’ve given ID of the template correctly
  2. If possible share us the simple Issue replicating sample
  3. Could you modify the given sample and replicate the issue which will be helpful for us to validate further
  4. Any console error that you’re facing
  5. Make sure that you’ve retrieved the data using the correct field


Sample Link : https://www.syncfusion.com/downloads/support/directtrac/general/ze/vue_composition-1683114689


UG LINK : https://ej2.syncfusion.com/vue/documentation/getting-started/vue-3-js-composition


Regards,

Udhayakumar



JK Jun Kim replied to Udhayakumar Anand September 11, 2023 06:05 PM UTC

Hello, sorry for the late reply.


Thanks you so for the example, with your help I was able to get the `taskbarTemplate` parameter working, but I was not able to get `labelSettings` still unfortunately.  I believe it not registering/rendering the template as component but still register as a string, but I don't know the issue still I almost copied the code example provide.


Image_1647_1694454889436


Here is the codebase I created (All sensitive data is censored and removed) I'm using to get this working. I want it to simply says the `data.name` and `data.EventType` as the left and right template, but it is still showing as `leftLabelTemplate` and `rightLabelTemplete` as shown above 


Line 58 is where I inputed the parameter for labelSettings:

:labelSettings="labelSettings"


Line 84-90 is where I rendering the template for labelSetting

          <template #leftLabelTemplate="{ data }">
            <div>{{ data.name }}</div>
          </template>
          <template #rightLabelTemplate="{ data }">
            <div>{{ data.EventType }}</div>
          </template>

Line 119-122 is where I created the constant


const labelSettings = {
  leftLabel: 'leftLabelTemplate',
  rightLabel: 'rightLabelTemplate',
};

Line 174-182 is where I created the column names: 

 const taskFields = ref({

      id: "EventID",

      name: "EventName",

      child: "ChildEvents",

      eventType: "EventType",

      startDate: "DateTimeStart",

      endDate: "DateTimeEnd",

      testResult: "TestResult",

    });


Line 391 is return it in the script:

 return {
      gantt,
      parts,
      overlay,
      dialog,
      selectedEvent,
      allEvents,
      data,
      dateFormat,
      taskFields,
      columns,
      splitterSettings,
      labelSettings,

Thanks for the help so far, hopefully we can get this working!


Thanks again,

Jun


Attachment: TimelineViewer_61c63c30.zip


UA Udhayakumar Anand Syncfusion Team September 12, 2023 01:32 PM UTC

Jun Kim


After a thorough review of your query and the provided sample, it has become apparent that the issue occurs from an incorrect placement of the label template and an attempt to access data using the wrong identifier. To rectify this, it is essential to enclose the template within the 'ejs-gantt' tag. Below, you can access the code snippet and the link to the modified sample for reference.


Code Snippet:

 <ejs-gantt

   <template #leftLabelTemplate="{ data }">

            <div>{{ data.EventName }}</div>

          </template>

          <template #rightLabelTemplate="{ data }">

            <div>{{ data.EventType }}</div>

          </template>

 

 </ejs-gantt>

 


Sample Link : https://www.syncfusion.com/downloads/support/directtrac/general/ze/vue_composition27720636


Regards,

Udhayakumar



JK Jun Kim replied to Udhayakumar Anand September 12, 2023 03:52 PM UTC

Wow such a simple mistake...



But everything works now,  thank you so much! much appreciated



SS Shereen Shajahan Syncfusion Team September 13, 2023 06:18 AM UTC

Hi Jun,

Glad that the solution works! Please get back to us for assistance in the future.

Regards,

Shereen


Loader.
Up arrow icon