How to Use dhtmlxGantt with Vue.js Framework [Demo]

| Comments (8)

What’s extremely awesome about our Gantt Chart library (besides fast performance and vast variety of features) is that it allows integrations with almost all new and popular frameworks and technologies. And today we’ll show you the easiest way to use js gantt chart with Vue.js, progressive JavaScript framework.

So, follow the instructions below to create a vue.js gantt chart or jump to a complete demo on GitHub right away.

New to dhtmlxGantt? Learn more about the library now

How We Start

The first thing we need to do is to get app skeleton. And for this, we’re going to use vue-cli. If you don’t have one, you can get it with node package manager using command (npm install -g vue). See this article.

To create an app the run following command:

vue init webpack-simple gantt-vue

It will request for some project info. You can just leave default answers and press enter button for each question.

Then you need to go to the app directory, install dependencies and run it.

cd gantt-vue
npm install
npm run dev

After these steps, the app should run on http://localhost:8080

vuejs-install

Moving to Gantt Chart Part

Now we should get the dhtmlxGantt code. Do so, run the following command:

npm install dhtmlx-gantt --save

Then, to add gantt chart to the application, we should create a component.

So, we’ll start with creating a folder for the app components. Open src folder and create components folder in it. Then, create Gantt.vue file in components folder and put the following code to it:

<template>
  <div ref="gantt"></div>
</template>

<script>
import 'dhtmlx-gantt'
export default {
  name: 'gantt',
  props: {
    tasks: {
      type: Object,
      default () {
        return {data: [], links: []}
      }
    }
  },

  mounted: function () {
    gantt.init(this.$refs.gantt)
    gantt.parse(this.$props.tasks)
  }
}
</script>

<style>
    @import "~dhtmlx-gantt/codebase/dhtmlxgantt.css";
</style>

Now, the gantt chart component is ready. When the element will be added to the page it will initialize gantt chart under “gantt” ref. Then, gantt chart will load data from tasks property.

And now it’s time to add the component to our app.

Open App.vue and add the following code instead that we’ve already had there.

<template>
  <div class="container">
    <gantt class="left-container" :tasks="tasks"></gantt>
  </div>
</template>

<script>
import Gantt from './components/Gantt.vue';

export default {
  name: 'app',
  components: {Gantt},
  data () {
    return {
      tasks: {
        data: [
          {id: 1, text: 'Task #1', start_date: '15-04-2017', duration: 3, progress: 0.6},
          {id: 2, text: 'Task #2', start_date: '18-04-2017', duration: 3, progress: 0.4}
        ],
        links: [
          {id: 1, source: 1, target: 2, type: '0'}
        ]
      },
    }
  }
}
</script>

Now, we should see the gantt chart with predefined tasks on a page.

gantt-vue

Listening changes and handling events

Let’s say we need to trace changes in gantt made by the user and process them somehow – show the details of selected item in a separate form, keep data model of parent component up to date, or send these changes to the backend. In other words, we need a way to let the rest of the app know what happens inside a gantt.

To do so we can capture API events of dhtmlxGantt and $emit them to the parent component.

As a simple demonstration, let’s implement a ‘changelog’ feature – we’ll write all changes made in gantt in a neat list somewhere on the page.

Firstly, go into gantt component and add code that will trace and emit changes of dhtmlxGantt. Add the following code right before gantt.init call:

    gantt.attachEvent('onAfterTaskAdd', (id, task) => {
      this.$emit('task-updated', id, 'inserted', task)
    })

    gantt.attachEvent('onAfterTaskUpdate', (id, task) => {
      this.$emit('task-updated', id, 'updated', task)
    })

    gantt.attachEvent('onAfterTaskDelete', (id) => {
      this.$emit('task-updated', id, 'deleted')
      if(!gantt.getSelectedId()) {
        this.$emit('task-selected', null)
      }
    })

    gantt.attachEvent('onAfterLinkAdd', (id, link) => {
      this.$emit('link-updated', id, 'inserted', link)
    })

    gantt.attachEvent('onAfterLinkUpdate', (id, link) => {
      this.$emit('link-updated', id, 'updated', link)
    })

    gantt.attachEvent('onAfterLinkDelete', (id, link) => {
      this.$emit('link-updated', id, 'deleted')
    })

It adds handlers to the add/update/delete events for the links and tasks. If some particular handler is called, it will trigger vue event on our component with parameters.

The next step is to add listeners for these events into app component and write a log of actions in another div.

Let’s extend the app component with the required functionality:

import Gantt from './components/Gantt.vue'

export default {
  name: 'app',
  components: {Gantt},
  data () {
    return {
      tasks: {
        data: [
          {id: 1, text: 'Task #1', start_date: '15-04-2017', duration: 3, progress: 0.6},
          {id: 2, text: 'Task #2', start_date: '18-04-2017', duration: 3, progress: 0.4}
        ],
        links: [
          {id: 1, source: 1, target: 2, type: '0'}
        ]
      },
      messages: []
    }
  },
  methods: {  
    addMessage (message) {
      this.messages.unshift(message)
      if(this.messages.length > 40) {
        this.messages.pop()
      }
    },

    logTaskUpdate (id, mode, task) {
      let text = (task && task.text ? ' (${task.text})': '')
      let message = 'Task ${mode}: ${id} ${text}'
      this.addMessage(message)
    },

    logLinkUpdate (id, mode, link) {
      let message = 'Link ${mode}: ${id}'
      if(link){
        message += ' ( source: ${link.source}, target: ${link.target} )'
      }
      this.addMessage(message)
    }
  }
}

What you can see here – we’ve added an array property where we’re going to store log entries, a method that adds a new message to the top of that array (our log will show new entries first). Also we’ve added two more methods that will create log messages for actions done with tasks and links and add them to message stack.

And finally update a template of the app component to utilize these functions:

 <template>
  <div class="container">
    <div class="right-container">
      <ul class="gantt-messages">
        <li class="gantt-message" v-for="message in messages">{{message}}</li>
      </ul>
    </div>
    <gantt class="left-container" :tasks="tasks" @task-updated="logTaskUpdate" @link-updated="logLinkUpdate"></gantt>
  </div>
</template>

We’ve added a simple two-column layout, attached our log handlers to the gantt events that we emit from gantt module, added a container for log messages and bind them to our log messages stack.

Now, if we make some changes to gantt, messages should be shown at the right side.

vuejs-gantt-chart

If you want to display some info of the selected tasks, proceed to the instructions below.

Open Gantt chart component and update the events handlers as follows:

    gantt.attachEvent('onTaskSelected', (id) => {
      let task = gantt.getTask(id)
      this.$emit('task-selected', task)
    })

    gantt.attachEvent('onAfterTaskAdd', (id, task) => {
      this.$emit('task-updated', id, 'inserted', task)
      task.progress = task.progress || 0
      if(gantt.getSelectedId() == id) {
        this.$emit('task-selected', task)
      }
    })

    gantt.attachEvent('onAfterTaskUpdate', (id, task) => {
      this.$emit('task-updated', id, 'updated', task)
    })

    gantt.attachEvent('onAfterTaskDelete', (id) => {
      this.$emit('task-updated', id, 'deleted')
      if(!gantt.getSelectedId()) {
        this.$emit('task-selected', null)
      }
    })

    gantt.attachEvent('onAfterLinkAdd', (id, link) => {
      this.$emit('link-updated', id, 'inserted', link)
    })

    gantt.attachEvent('onAfterLinkUpdate', (id, link) => {
      this.$emit('link-updated', id, 'updated', link)
    })

    gantt.attachEvent('onAfterLinkDelete', (id, link) => {
      this.$emit('link-updated', id, 'deleted')
    })

Here we’ve added onTaskSelected handler that is going to trigger ‘task-selected’ event. Also we should trigger it after inserting and deleting the task.

Open the app component to add selection handler to it. Also we need to add some necessary elements to our template as well. It should look like this:

<template>
  <div class="container">
    <div class="right-container">
      <div class="gantt-selected-info">
        <div v-if="selectedTask">
          <h2>{{selectedTask.text}}</h2>
          <span><b>ID: </b>{{selectedTask.id}}</span><br/>
          <span><b>Progress: </b>{{selectedTask.progress|toPercent}}%</span><br/>
          <span><b>Start Date: </b>{{selectedTask.start_date|niceDate}}</span><br/>
          <span><b>End Date: </b>{{selectedTask.end_date|niceDate}}</span><br/>
        </div>
        <div v-else class="select-task-prompt">
          <h2>Click any task</h2>
        </div>
      </div>
      <ul class="gantt-messages">
        <li class="gantt-message" v-for="message in messages">{{message}}</li>
      </ul>
    </div>
    <gantt class="left-container" :tasks="tasks" @task-updated="logTaskUpdate" @link-updated="logLinkUpdate" @task-selected="selectTask"></gantt>
  </div>
</template>

Here we’ve added another container which is bind to selectedTask property of the app component using “v-if” directive. Also, we’ve added a handler for “task-selected” event we now emit.

Make sure to add this property to the app component:

      selectedTask: null

And add selectTask method which is used in handler we’ve defined above:

    selectTask: function(task){
      this.selectedTask = task
    }

Thus, each time user selects task inside gantt, the component emits ‘task-selected’ event. Then this event is captured by the app component. Inside an event handler, we update selectedTask property, which in its turn invokes a repaint .gantt-selected-info element with the task details.

Note that the task object has start_date/end_date properties of Date type and progress completion in float type – these should be formatted in human-friendly form before added to the page.

It’s implemented using toPercent and niceDate filters which we define like this:

  filters: {
    toPercent (val) {
      if(!val) return '0'
      return Math.round((+val) * 100)
    },
    niceDate (obj){
      return '${obj.getFullYear()} / ${obj.getMonth()} / ${obj.getDate()}'
    }
  }

Now, if we run our app and select some task, we should see that its info shows on the right.

Gantt-chart-vuejs

So, we’ve created a simple Gantt chart with the help of dhtmlxGantt and Vue.js. The results of our work can be found on GitHub. If you follow the instructions above and meet any difficulties, don’t hesitate to share them with us.

Which technologies/frameworks are you using?

We need your feedback to provide the right integrations at the right time. Please leave your requests here:

Thank you in advance and stay tuned for new tutorials!

Comments

  1. Ray Peterson May 20, 2017 at 7:19 pm

    Hi! Well done, many thanks! Do you plan to make a guide for ReactJS? It would be very helpful!

    • Aras Kairys (DHTMLX team) May 21, 2017 at 7:23 pm

      Hi Ray,
      Thank you for your feedback.
      You can use “request integration” button at the bottom of the article to vote for the next guide.

    • Aras Kairys (DHTMLX team) May 24, 2017 at 9:30 pm

      Hi Ray,
      We are happy to inform you that the guide about the integration of dhtmlxGantt and ReactJS is ready.
      Please check it here: https://dhtmlx.com/blog/create-react-gantt-chart-component-dhtmlxgantt

  2. Hu Xiaode June 9, 2017 at 5:56 pm

    Nice and clear, thank you!

  3. Valerio C. July 13, 2017 at 12:37 pm

    Helpful guide. Very easy integration!

    • Aras Kairys (DHTMLX team) July 13, 2017 at 12:42 pm

      Thank you, Valerio!

  4. July 16, 2017 at 12:02 pm

    hello, this gantt components can use Chinese in Vue? how to make it work? wish you reply! thanks!

    • Aras Kairys (DHTMLX team) July 19, 2017 at 1:36 pm

      Hello,
      Yes, dhtmlxGantt can use Chinese. You need to import the appropriate locale file from dhtmlxGantt package, e.g.:
      import “~dhtmlx-gantt/codebase/locale/locale_cn”;

Leave a Reply