Work Breakdown Structure (WBS) in Vue Gantt Component

28 Jun 202522 minutes to read

The Work Breakdown Structure (WBS) organizes project tasks hierarchically in the Gantt component by assigning unique codes to each task. This system enhances visualization and management by clearly reflecting task relationships and levels. It is especially useful in complex environments like construction projects or enterprise-scale software development.


Configuration and implementation

To enable and configure WBS in your Gantt component:

  • Enable WBS Codes: Set the enableWBS property to true to automatically generate unique task codes and their predecessors.
  • Auto-Update Codes: Set the enableAutoWbsUpdate property to true to maintain WBS code accuracy during operations like sorting, filtering, editing, or drag-and-drop.
<template>
  <div class="content-wrapper">
    <ejs-gantt
      ref="gantt"
      id="HierarchyProjectview"
      :dataSource="projectData"
      :taskFields="taskFields"
      :columns="columns"
      :editSettings="editSettings"
      :toolbar="toolbar"
      :allowSorting="true"
      :enableContextMenu="true"
      :enableWBS="enableWBS"
      :treeColumnIndex="2"
      :enableAutoWbsUpdate="enableAutoWbsUpdate"
      :allowFiltering="true"
      :filterSettings="filterSettings"
      :selectionSettings="selectionSettings"
      :splitterSettings="splitterSettings"
      :gridLines="'Both'"
      :highlightWeekends="true"
      :timelineSettings="timelineSettings"
      :labelSettings="labelSettings"
      :taskbarHeight="20"
      :rowHeight="40"
      :eventMarkers="eventMarkers"
      :height="'550px'"
      :allowUnscheduledTasks="true"
      :projectStartDate="projectStartDate"
      :projectEndDate="projectEndDate"
    />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue';
import { GanttComponent as EjsGantt, Selection, Toolbar, DayMarkers, Edit, Filter, Sort, ContextMenu } from '@syncfusion/ej2-vue-gantt';
import { WBSData } from './data-source';

const gantt = ref(null);
provide('gantt', [Selection, Toolbar, DayMarkers, Edit, Filter, Sort, ContextMenu]);

const enableWBS = true;
const enableAutoWbsUpdate = true;
const projectData = WBSData;
const projectStartDate = new Date('03/31/2024');
const projectEndDate = new Date('05/30/2024');

const taskFields = {
  id: 'TaskID',
  name: 'TaskName',
  startDate: 'StartDate',
  duration: 'Duration',
  progress: 'Progress',
  dependency: 'Predecessor',
  parentID: 'ParentId',
};

const columns = [
  { field: 'TaskID', headerText: 'Task ID', visible: false },
  { field: 'WBSCode', headerText: 'WBS Code', width: '150px' },
  { field: 'TaskName', headerText: 'Task Name', allowReordering: false, width: '260px' },
  { field: 'StartDate', headerText: 'Start Date', width: '140px' },
  { field: 'WBSPredecessor', headerText: 'WBS Predecessor', width: '190px' },
  { field: 'Duration', headerText: 'Duration', allowEditing: false, width: '130px' },
  { field: 'Progress', headerText: 'Progress' },
];

const toolbar = ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll'];

const filterSettings = { type: 'Menu' };
const selectionSettings = { mode: 'Row', type: 'Single', enableToggle: false };
const splitterSettings = { columnIndex: 4 };

const timelineSettings = {
  showTooltip: true,
  topTier: { unit: 'Week', format: 'dd/MM/yyyy' },
  bottomTier: { unit: 'Day', count: 1 },
};

const labelSettings = {
  leftLabel: 'TaskID',
  rightLabel: 'Task Name: ${taskData.TaskName}',
  taskLabel: '${Progress}%',
};

const eventMarkers = [{ day: new Date('04/02/2024'), label: 'Project Initiation' }];
</script>
<template>
  <div class="content-wrapper">
    <ejs-gantt
      id="HierarchyProjectview"
      :dataSource="projectData"
      :taskFields="taskFields"
      :columns="columns"
      :editSettings="editSettings"
      :toolbar="toolbar"
      :allowSorting="true"
      :enableContextMenu="true"
      :enableWBS="enableWBS"
      :treeColumnIndex="2"
      :enableAutoWbsUpdate="enableAutoWbsUpdate"
      :allowFiltering="true"
      :filterSettings="filterSettings"
      :selectionSettings="selectionSettings"
      :splitterSettings="splitterSettings"
      :gridLines="'Both'"
      :highlightWeekends="true"
      :timelineSettings="timelineSettings"
      :labelSettings="labelSettings"
      :taskbarHeight="20"
      :rowHeight="40"
      :eventMarkers="eventMarkers"
      :height="'550px'"
      :allowUnscheduledTasks="true"
      :projectStartDate="projectStartDate"
      :projectEndDate="projectEndDate"
    />
  </div>
</template>

<script>
import { GanttComponent, Selection, Toolbar, DayMarkers, Edit, Filter, Sort, ContextMenu } from '@syncfusion/ej2-vue-gantt';
import { WBSData } from './data-source';

export default {
  name: 'App',
  components: {
    'ejs-gantt': GanttComponent,
  },
  data() {
    return {
      enableWBS: true,
      enableAutoWbsUpdate: true,
      projectData: WBSData,
      projectStartDate: new Date('03/31/2024'),
      projectEndDate: new Date('05/30/2024'),
      taskFields: {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        dependency: 'Predecessor',
        parentID: 'ParentId',
      },
      columns: [
        { field: 'TaskID', headerText: 'Task ID', visible: false },
        { field: 'WBSCode', headerText: 'WBS Code', width: '150px' },
        { field: 'TaskName', headerText: 'Task Name', allowReordering: false, width: '260px' },
        { field: 'StartDate', headerText: 'Start Date', width: '140px' },
        { field: 'WBSPredecessor', headerText: 'WBS Predecessor', width: '190px' },
        { field: 'Duration', headerText: 'Duration', allowEditing: false, width: '130px' },
        { field: 'Progress', headerText: 'Progress' },
      ],
      toolbar: ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll'],
      filterSettings: { type: 'Menu' },
      selectionSettings: { mode: 'Row', type: 'Single', enableToggle: false },
      splitterSettings: { columnIndex: 4 },
      timelineSettings: {
        showTooltip: true,
        topTier: { unit: 'Week', format: 'dd/MM/yyyy' },
        bottomTier: { unit: 'Day', count: 1 },
      },
      labelSettings: {
        leftLabel: 'TaskID',
        rightLabel: 'Task Name: ${taskData.TaskName}',
        taskLabel: '${Progress}%',
      },
      eventMarkers: [{ day: new Date('04/02/2024'), label: 'Project Initiation' }],
    };
  },
  provide: {
    gantt: [Selection, Toolbar, DayMarkers, Edit, Filter, Sort, ContextMenu],
  },
};
</script>


Managing WBS code updates

For better performance, you can control when WBS codes are updated by using the actionBegin and dataBound events. This is particularly useful during actions like dragging and dropping rows.

In the following example, WBS auto-update is enabled only during the row drag and drop action using these events.

<template>
  <div class="content-wrapper">
    <ejs-gantt
      ref="gantt"
      id="HierarchyProjectview"
      :dataSource="projectData"
      :taskFields="taskFields"
      :columns="columns"
      :editSettings="editSettings"
      :toolbar="toolbar"
      :allowSorting="true"
      :enableContextMenu="true"
      :enableWBS="true"
      :treeColumnIndex="2"
      :enableAutoWbsUpdate="enableAutoWbsUpdate"
      :allowFiltering="true"
      :filterSettings="filterSettings"
      :selectionSettings="selectionSettings"
      :splitterSettings="splitterSettings"
      :gridLines="'Both'"
      :highlightWeekends="true"
      :timelineSettings="timelineSettings"
      :labelSettings="labelSettings"
      :taskbarHeight="20"
      :rowHeight="40"
      :eventMarkers="eventMarkers"
      :height="'550px'"
      :allowUnscheduledTasks="true"
      :projectStartDate="projectStartDate"
      :projectEndDate="projectEndDate"
      @actionBegin="actionBegin"
      @dataBound="dataBound"
    />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue';
import {
  GanttComponent as EjsGantt,
  Selection,
  Toolbar,
  DayMarkers,
  Edit,
  Filter,
  Sort,
  ContextMenu
} from '@syncfusion/ej2-vue-gantt';
import { WBSData } from './data-source';

provide('gantt', [Selection, Toolbar, DayMarkers, Edit, Filter, Sort, ContextMenu]);

const gantt = ref(null);
const isRowDropped = ref(false);
const enableAutoWbsUpdate = ref(false);
const projectData = WBSData;

const taskFields = {
  id: 'TaskID',
  name: 'TaskName',
  startDate: 'StartDate',
  duration: 'Duration',
  progress: 'Progress',
  dependency: 'Predecessor',
  parentID: 'ParentId',
};

const columns = [
  { field: 'TaskID', visible: false },
  { field: 'WBSCode', headerText: 'WBS Code', width: '150px' },
  { field: 'TaskName', headerText: 'Task Name', width: '260px' },
  { field: 'StartDate', headerText: 'Start Date', width: '140px' },
  { field: 'WBSPredecessor', headerText: 'WBS Predecessor', width: '190px' },
  { field: 'Duration', headerText: 'Duration', allowEditing: false, width: '130px' },
  { field: 'Progress', headerText: 'Progress' }
];

const editSettings = {
  allowAdding: true,
  allowEditing: true,
  allowDeleting: true,
  allowTaskbarEditing: true,
  showDeleteConfirmDialog: true
};

const toolbar = ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll'];
const filterSettings = { type: 'Menu' };
const selectionSettings = { mode: 'Row', type: 'Single' };
const splitterSettings = { columnIndex: 4 };
const timelineSettings = {
  topTier: { unit: 'Week', format: 'dd/MM/yyyy' },
  bottomTier: { unit: 'Day', count: 1 },
  showTooltip: true
};

const labelSettings = {
  leftLabel: 'TaskID',
  rightLabel: 'Task Name: ${taskData.TaskName}',
  taskLabel: '${Progress}%'
};

const eventMarkers = [{ day: new Date('04/02/2024'), label: 'Project Initiation' }];
const projectStartDate = new Date('03/31/2024');
const projectEndDate = new Date('05/30/2024');

function actionBegin(args) {
  if (args.requestType === 'beforeDrop') {
    isRowDropped.value = true;
    gantt.value.ej2Instances.enableAutoWbsUpdate = true;
  }
}

function dataBound() {
  if (isRowDropped.value) {
    gantt.value.ej2Instances.enableAutoWbsUpdate = false;
    isRowDropped.value = false;
  }
}
</script>
<template>
  <div class="content-wrapper">
    <ejs-gantt
      ref="gantt"
      id="HierarchyProjectview"
      :dataSource="projectData"
      :taskFields="taskFields"
      :columns="columns"
      :editSettings="editSettings"
      :toolbar="toolbar"
      :allowSorting="true"
      :enableContextMenu="true"
      :enableWBS="true"
      :treeColumnIndex="2"
      :enableAutoWbsUpdate="enableAutoWbsUpdate"
      :allowFiltering="true"
      :filterSettings="filterSettings"
      :selectionSettings="selectionSettings"
      :splitterSettings="splitterSettings"
      :gridLines="'Both'"
      :highlightWeekends="true"
      :timelineSettings="timelineSettings"
      :labelSettings="labelSettings"
      :taskbarHeight="20"
      :rowHeight="40"
      :eventMarkers="eventMarkers"
      :height="'550px'"
      :allowUnscheduledTasks="true"
      :projectStartDate="projectStartDate"
      :projectEndDate="projectEndDate"
      @actionBegin="actionBegin"
      @dataBound="dataBound"
    />
  </div>
</template>

<script>
import {
  GanttComponent,
  Selection,
  Toolbar,
  DayMarkers,
  Edit,
  Filter,
  Sort,
  ContextMenu
} from '@syncfusion/ej2-vue-gantt';
import { WBSData } from './data-source';

export default {
  components: {
    'ejs-gantt': GanttComponent
  },
  data() {
    return {
      isRowDropped: false,
      enableAutoWbsUpdate: false,
      projectData: WBSData,
      taskFields: {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        dependency: 'Predecessor',
        parentID: 'ParentId'
      },
      columns: [
        { field: 'TaskID', visible: false },
        { field: 'WBSCode', headerText: 'WBS Code', width: '150px' },
        { field: 'TaskName', headerText: 'Task Name', width: '260px' },
        { field: 'StartDate', headerText: 'Start Date', width: '140px' },
        { field: 'WBSPredecessor', headerText: 'WBS Predecessor', width: '190px' },
        { field: 'Duration', headerText: 'Duration', allowEditing: false, width: '130px' },
        { field: 'Progress', headerText: 'Progress' }
      ],
      editSettings: {
        allowAdding: true,
        allowEditing: true,
        allowDeleting: true,
        allowTaskbarEditing: true,
        showDeleteConfirmDialog: true
      },
      toolbar: ['Add', 'Edit', 'Update', 'Delete', 'Cancel', 'ExpandAll', 'CollapseAll'],
      filterSettings: { type: 'Menu' },
      selectionSettings: { mode: 'Row', type: 'Single' },
      splitterSettings: { columnIndex: 4 },
      timelineSettings: {
        topTier: { unit: 'Week', format: 'dd/MM/yyyy' },
        bottomTier: { unit: 'Day', count: 1 },
        showTooltip: true
      },
      labelSettings: {
        leftLabel: 'TaskID',
        rightLabel: 'Task Name: ${taskData.TaskName}',
        taskLabel: '${Progress}%'
      },
      eventMarkers: [{ day: new Date('04/02/2024'), label: 'Project Initiation' }],
      projectStartDate: new Date('03/31/2024'),
      projectEndDate: new Date('05/30/2024')
    };
  },
  provide: {
    gantt: [Selection, Toolbar, DayMarkers, Edit, Filter, Sort, ContextMenu]
  },
  methods: {
    actionBegin(args) {
      if (args.requestType === 'beforeDrop') {
        this.isRowDropped = true;
        this.$refs.gantt.ej2Instances.enableAutoWbsUpdate = true;
      }
    },
    dataBound() {
      if (this.isRowDropped) {
        this.$refs.gantt.ej2Instances.enableAutoWbsUpdate = false;
        this.isRowDropped = false;
      }
    }
  }
};
</script>


Limitations

The WBS feature has a few limitations in the Vue Gantt component:

  • Editing of the WBS code and WBS predecessor columns is not supported.
  • Load on demand is not supported with the WBS feature.
  • WBS Code and WBS Predecessor fields cannot be mapped directly from the data source.