Bryntum
10 November 2023

How to connect and sync Bryntum Gantt to monday.com

With Bryntum Gantt, you can build superfast and fully customizable Gantt charts with pure JavaScript. You can easily use Bryntum Gantt […]

With Bryntum Gantt, you can build superfast and fully customizable Gantt charts with pure JavaScript. You can easily use Bryntum Gantt with React, Vue, or Angular.

In this tutorial, we’ll connect and sync Bryntum Gantt to monday.com. We’ll do the following:

Getting started

Clone the starter GitHub repository. This starter repository uses development server and JavaScript bundler Vite. You’ll need Node.js version 14.18+ for Vite to work.

Install the Vite dev dependency by running the following command:

npm install

The dev server is configured to run on http://localhost:8080/ in the vite.config.js file. Run the local dev server now using npm run dev and you’ll see a blank page.

Create a Gantt chart using Bryntum Gantt

We’ll install the Bryntum Gantt component using npm. Follow step one of the Bryntum Gantt set up guide to log into the Bryntum registry component.

Then initialize your application using the npm CLI command:

npm init

You will be asked a series of questions in the terminal; accept all the defaults by pressing the enter key for each question.

Now follow step four of the Bryntum Gantt set up guide to install Bryntum Gantt.

Let’s import the Bryntum Gantt component and give it some basic configuration. In the main.js file, add the following lines:

import { Gantt } from '@bryntum/gantt/gantt.module.js';
const gantt = new Gantt({
    appendTo: document.body,
    // startDate is today and endDate is 1 year from today
    startDate: new Date(),
    endDate: new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()),
    project: {
        tasksData: [
            {
                id: 1,
                name: 'Write docs',
                expanded: true,
                children: [
                    {
                        id: 2,
                        name: 'Proof-read docs',
                        startDate: '2023-01-02',
                        endDate: '2023-01-09',
                        manuallyScheduled: true
                    },
                    {
                        id: 3,
                        name: 'Release docs',
                        startDate: '2023-01-09',
                        endDate: '2023-01-10',
                        manuallyScheduled: true
                    }
                ]
            }
        ],
    },
    columns: [
        { type: 'name', width: 160 }
    ]
});

Here we import Bryntum Gantt, create a new Bryntum Gantt instance, and pass a configuration object into it. We add the Gantt chart to the <body> of the DOM.

You can set your Gantt chart up to display specific dates on opening. In this example, we set the startDate to the current date and endDate to one year from today, but you can change these dates to whatever applies to your project.

We pass in data inline to populate TaskData for simplicity. We have a parent item with two children items within it. You can learn more about working with data in the Bryntum docs.

If you run your dev server now, you’ll see the items in our Bryntum Gantt chart:

Our Gantt chart uses the Stockholm theme, which is one of five available themes for Bryntum Gantt charts. We’ve linked to the CSS for the Stockholm theme in index.html. If you’d like to create a custom theme, read more about styling your Gantt chart in our customization documentation.

Now let’s learn how to retrieve a list of monday.com items from a user’s account using the monday.com GraphQL API.

Set up monday.com

You will need an account on monday.com. Sign up or log in to your monday.com account. If you’re setting up a new account, choose “items” when asked what you would like to manage and select the default configuration for the rest of the set up.

Once you’ve accessed your account, you’ll see a default table containing some items. To get monday.com to work as a Gantt chart, we’ll need to change the way the default “Date” columns work.

To do this, click the three dots on the “Date” column, click “Change column type”, and then select “Timeline”.

A new “Date” column will be generated. Delete the old column. The new “Date” column allows you to assign a timespan as a date to items, which is useful when organizing projects and due dates.

Now do the same for a subitem by clicking the arrow on the left of the top item and repeating the steps for the subitem.

Get your access token

You’ll need an access token to access the items in your monday.com table for your Bryntum Gantt chart. Click on the profile picture in the bottom left corner of the screen and then click on “Developers”.

Next, click on the “Developer” button at the top of the screen and select the “My Access Tokens” menu item.

In the developers section, you will find the access token needed to query the monday.com API. Take down this access token because we will need it later.

Use monday.com GraphQL API to access a your monday.com items

Now that we have an access token, we can use it to query the monday.com API and retrieve all the items from our boards.

In the graph.js file, add the code below (make sure to replace <your-access-token> with the access token you received earlier – note that for production you should rather keep your access token in an environment variable, but we’ve hard-coded it in these examples for simplicity).

import { createGantt, updateChildrenList, updateParentList } from "./main.js";
function getTasksFromMonday() {
    const query = '{boards(limit:2) { name id description items { name id parent_item{id} column_values{ title id type text } } } }';
    fetch("https://api.monday.com/v2", {
        method: 'post',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': '<your-access-token>'
        },
        body: JSON.stringify({
            'query': query
        })
    })
        .then((result) => result.json())
        .then(res => {
            let eventList = [];
            eventList = updateParentList(res, eventList);
            eventList = updateChildrenList(res, eventList);
            createGantt(eventList);
        });
}
export { getTasksFromMonday };

Here we define a function called getTasksFromMonday that makes a query to the monday.com GraphQL API. The query is limited to two boards and returns the name, ID, description, and items from the board and the name, ID, parent item ID, and column values of each item. We will use these values to define the items that are added to our Gantt chart.

The query then initiates a few functions. The updateParentList function adds the parent level items to a list called eventList, then a function called updateChildrenList adds the subitems to their appropriate parent items and returns the updated eventList. Finally createGantt is called which will use the data from the eventList to create the Gantt instance and TaskData.

Replace all the code in the main.js file with the following code:

import { Gantt } from '@bryntum/gantt/gantt.module.js';
import { getTasksFromMonday } from './graph.js';
var isAppended = false;
getTasksFromMonday();
function createGantt(eventList) {
    const columnIdList = {
        parent: eventList[0].column_values[3].id
    };
    const parentBoardId = eventList[0].board_id;
    const gantt = new Gantt({
        startDate: new Date(),
        endDate: new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()),
        listeners: {
            dataChange: function (event) {
                updateMonday(event);
            }
        },
        project: {
            tasksData: eventList,
        },
        column_ids: columnIdList,
        board_id: parentBoardId,
        columns: [
            { type: 'name', width: 160 }
        ]
    });
    if (isAppended === false) {
        gantt.appendTo = document.body;
        isAppended = true;
    }
}
export { createGantt, updateParentList, updateChildrenList };

This is the createGantt function that will create our Gantt instance. We use the isAppended variable to ensure the Gantt isn’t appended twice on Vite’s page reload.

Now let’s add the updateParentList to our code, below the createGantt function:

function updateParentList(results, eventList) {
    const parentBoard = results.data.boards[1];
    for (let j = 0; j < parentBoard.items.length; j++) {
        eventList.push({
            id: parentBoard.items[j].id,
            monday_id: parentBoard.items[j].id,
            name: parentBoard.items[j].name,
            startDate: parentBoard.items[j].column_values[3].text.slice(0, 10),
            endDate: parentBoard.items[j].column_values[3].text.slice(13, 23),
            expanded: false,
            children: [],
            board_id: parentBoard.id,
            column_values: parentBoard.items[j].column_values,
        });
    }
    return eventList;
}

Here the updateParentList function loops through each of the boards that were returned from the getTasksFromMonday query. Each item from the parent board is added to the eventList.

Now let’s add all subitems to their parent item with the updateChildrenList function. Add these lines below the updateParentList function:

function updateChildrenList(results, eventList) {
    const childBoard = results.data.boards[0];
    for (let j = 0; j < childBoard.items.length; j++) {
        if (childBoard.items[j].parent_item != null) {
            const parentId = childBoard.items[j].parent_item.id;
            const childId = childBoard.items[j].id;
            for (var k = 0; k < eventList.length; k++) {
                if (parentId === eventList[k].id) {
                    eventList[k].children.push({
                        id: childId,
                        monday_id: childId,
                        name: childBoard.items[j].name,
                        startDate: childBoard.items[j].column_values[2].text.slice(0, 10),
                        endDate: childBoard.items[j].column_values[2].text.slice(13, 23),
                        board_id: childBoard.id,
                        column_values: childBoard.items[j].column_values,
                        manuallyScheduled: true
                    });
                    eventList[k].expanded = true;
                    eventList[k].endDate = childBoard.items[j].column_values[2].text.slice(13, 23);
                }
            }
        }
    }
    return eventList;
}

The subitems are looped through and added to the appropriate parent item’s children list. These events are then passed to createGantt, which will add these items to the Gantt chart and append the chart to the body of the DOM.

If you run the server now, you will see the items from your monday.com boards reflected in the Bryntum Gantt chart.

Implementing CRUD

Now that our Gantt chart has received all the items from monday.com, we’ll implement the rest of the CRUD functionality by taking advantage of monday.com’s API.

Each of these functions requires your access token, so make sure to replace <your-access-token> with the access token you retrieved earlier.

Create events

In the graph.js file, add the following lines:

function addParentTaskToMonday(event, parentName, parentStart, parentEnd) {
    // add a parent task to monday.com with the given parameters
    let columnValues = `{\"${event.source.column_ids.parent}\" : {\"from\" : \"${parentStart}\", \"to\": \"${parentEnd}\"}}`;
    columnValues = JSON.stringify(columnValues);
    const query = `mutation{ create_item (board_id: ${event.source.column_ids.child}, item_name: \"${parentName}\", create_labels_if_missing: true, column_values: ${columnValues}){ id board{id} column_values{title id type text } }}`;
    fetch("https://api.monday.com/v2", {
        method: 'post',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': '<your-access-token>'
        },
        body: JSON.stringify({
            'query': query
        })
    })
        .then((result) => result.json())
        .then(res => {
            event.records[0]._data.monday_id = res.data.create_item.id;
            event.records[0]._data.board_id = res.data.create_item.board.id;
            event.records[0]._data.column_values = res.data.create_item.column_values;
            event.records[0]._data.manuallyScheduled = true;
        })
}

Here we define addParentTaskToMonday, which takes in data from a parent item, uses this data to query the monday.com API, and creates a parent-level item. The event represents an object that will be passed from a listener, which we will set up later. The event is used here to update the Gantt item with the details from the query result of the created item.

Now we will define the function to create the subitems. In the graph.js file, add the following lines:

function addTaskToMonday(event, parentId, childName, childStart, childEnd) {
    // add a task to monday.com with the given parameters
    let columnValues = `{\"${event.source.column_ids.child}\" : {\"from\" : \"${childStart}\", \"to\": \"${childEnd}\"}}`;
    columnValues = JSON.stringify(columnValues);
    const query = `mutation{ create_subitem (parent_item_id: ${parentId}, item_name: \"${childName}\", create_labels_if_missing: true, column_values: ${columnValues}){ id board{id} parent_item{id} column_values{title id type text } }}`;
    fetch("https://api.monday.com/v2", {
        method: 'post',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': '<your-access-token>'
        },
        body: JSON.stringify({
            'query': query
        })
    })
        .then((result) => result.json())
        .then(res => {
                event.records[0]._data.monday_id = res.data.create_subitem.id;
                event.records[0]._data.board_id = res.data.create_subitem.board.id;
                event.records[0]._data.parentId = res.data.create_subitem.parent_item.id;
                event.records[0]._data.column_values = res.data.create_subitem.column_values;
                event.records[0]._data.manuallyScheduled = true;
            }
        );
}

Similarly to our function to add a parent item, addTaskToMonday takes in data from the appropriate Gantt chart item, uses the data to query the monday.com API, and creates a subitem-level item. The event is used to update the Gantt item with the details from the query result of the created item.

Update events

In the graph.js file, add the following function:

function updateTaskOnMonday(boardId, columnId, childId, childName, childStart, childEnd, updateType) {
    if (updateType === "timeline") {
        var columnValues = `{\"${columnId}\" : {\"from\" : \"${childStart}\", \"to\" : \"${childEnd}\"}}`
    } else if (updateType === "name") {
        var columnValues = `{\"name\" : \"${childName}\"}`
    }
    columnValues = JSON.stringify(columnValues);
    const query = `mutation{ change_multiple_column_values (board_id: ${boardId}, item_id: ${childId}, column_values: ${columnValues}){ id }}`;
    fetch("https://api.monday.com/v2", {
        method: 'post',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': '<your-access-token>'
        },
        body: JSON.stringify({
            'query': query
        })
    })
        .then((result) => result.json());
}

The updateTaskOnMonday function uses the updateType variable to determine which columns to query for the update. Either the “Date” column or the “Name” column will be updated and the appropriate query will be constructed.

Delete events

In the graph.js file, add the following function:

function deleteTask(id) {
    // delete a task on monday.com with the given parameters
    const query = `mutation{ delete_item (item_id: ${id}){ id }}`;
    fetch("https://api.monday.com/v2", {
        method: 'post',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': '<your-access-token>'
        },
        body: JSON.stringify({
            'query': query
        })
    })
}

The deleteTask function will identify the appropriate item by id and delete the item.

Now replace the exports at the bottom of graph.js with the following line:

export { getTasksFromMonday, addTaskToMonday, addParentTaskToMonday, updateTaskOnMonday, deleteTask };

Listening for event data changes in Bryntum Gantt

Next, we’ll set the listeners for our Bryntum Gantt so that it will know when the user updates the Gantt events.

Replace the definition of gantt with the following code:

const gantt = new Gantt({
    // startDate is today and endDate is 1 year from today
    startDate: new Date(),
    endDate: new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()),
    listeners: {
        dataChange: function (event) {
            updateMonday(event);
        }
    },
    project: {
        tasksData: eventList,
    },
    column_ids: columnIdList,
    columns: [
        { type: 'name', width: 160 }
    ]
});

Here we set a listener on our Bryntum Gantt to listen for any changes to the Gantt’s data store. This will fire an "add" event whenever a Gantt item is created, an "update" event when an item is updated, and a "remove" event whenever an item is deleted.

The item retrieved from the dataChange listener also carries event data about the specific Gantt item that has been altered. We’ll use the event data to identify which item is being altered and what’s being changed.

Next we’ll create a function called updateMonday that will update monday.com when the appropriate "add""update", or "delete" event is fired.

Add the following code below the createGantt function in the main.js file:

function updateMonday(event) {
    if (event.action === "add" && event.isChild === true) {
        if (event.records[0]._data.parentId != null) {
            const parentId = event.parent._data.monday_id;
            const childName = event.records[0]._data.name;
            const childStart = event.records[0]._data.startDate.toISOString().split('T')[0];
            const childEnd = event.records[0]._data.endDate.toISOString().split('T')[0];
            addTaskToMonday(event, parentId, childName, childStart, childEnd);
        } else {
            const parentName = event.records[0]._data.name;
            const parentStart = event.records[0]._data.startDate.toISOString().split('T')[0];
            const parentEnd = event.records[0]._data.endDate.toISOString().split('T')[0];
            addParentTaskToMonday(event, parentName, parentStart, parentEnd);
        }
    } else if (event.action === "update") {
        if ("startDate" in event.changes || "endDate" in event.changes || "name" in event.changes) {
            if ("column_values" in event.records[0]._data) {
                if ("name" in event.changes) {
                    var updateType = "name";
                } else if ("startDate" in event.changes || "endDate" in event.changes) {
                    var updateType = "timeline";
                }
                const taskId = event.record._data.monday_id;
                const taskName = event.record._data.name;
                const taskStart = event.record._data.startDate.toISOString().split('T')[0];
                const taskEnd = event.record._data.endDate.toISOString().split('T')[0];
                const boardId = event.record._data.board_id;
                const columnId = event.record._data.column_values[event.record._data.column_values.length - 1].id;
                updateTaskOnMonday(boardId, columnId, taskId, taskName, taskStart, taskEnd, updateType);
            }
        }
    } else if (event.action === "remove") {
        if (event.isCollapse !== true) {
            const taskId = event.records[0]._data.monday_id;
            deleteTask(taskId);
        }
    }
}

Here we create a function that is called on all changes to the data store of Bryntum Gantt, which then calls one of the monday.com API CRUD functions we defined.

On "add", we check whether the item being created is a parent item or a subitem. If the item is a subitem, then addTaskToMonday is called with data from the event and the item is created on monday.com.

function updateMonday(event) {
    if (event.action === "add" && event.isChild === true) {
        if (event.records[0]._data.parentId != null) {
            const parentId = event.parent._data.monday_id;
            const childName = event.records[0]._data.name;
            const childStart = event.records[0]._data.startDate.toISOString().split('T')[0];
            const childEnd = event.records[0]._data.endDate.toISOString().split('T')[0];
            addTaskToMonday(event, parentId, childName, childStart, childEnd);

If the item is a parent item, then addParentTaskToMonday is called with data from the event and the item is created on monday.com.

        } else {
            const parentName = event.records[0]._data.name;
            const parentStart = event.records[0]._data.startDate.toISOString().split('T')[0];
            const parentEnd = event.records[0]._data.endDate.toISOString().split('T')[0];
            addParentTaskToMonday(event, parentName, parentStart, parentEnd);
        }

On "update", we check whether the update is relevant to our monday.com items. If the update includes "startDate""endDate", or "name" data and data for "column_values", then the task is a monday.com item. Then we establish the updateType so that the appropriate query can be called in the updateTaskOnMonday function.

    } else if (event.action == "update") {
        if ("startDate" in event.changes || "endDate" in event.changes || "name" in event.changes) {
            if ("column_values" in event.records[0]._data) {
                if ("name" in event.changes) {
                    var updateType = "name";
                } else if ("startDate" in event.changes || "endDate" in event.changes) {
                    var updateType = "timeline";
                }
                const taskId = event.record._data.monday_id;
                const taskName = event.record._data.name;
                const taskStart = event.record._data.startDate.toISOString().split('T')[0];
                const taskEnd = event.record._data.endDate.toISOString().split('T')[0];
                const boardId = event.record._data.board_id;
                const columnId = event.record._data.column_values[event.record._data.column_values.length - 1].id;
                updateTaskOnMonday(boardId, columnId, taskId, taskName, taskStart, taskEnd, updateType);
            }
        }
    }

Finally, if the dataChange event is a "remove" event, then we delete the matching monday.com item using the deleteTask function.

    } else if (event.action == "remove") {
        if (event.isCollapse != true) {
            const taskId = event.records[0]._data.monday_id;
            deleteTask(taskId);
        }
    }

Finally, change the imports at the top of the main.js file to the following:

import { Gantt } from '@bryntum/gantt/gantt.module.js';
import { getTasksFromMonday, addTaskToMonday, updateTaskOnMonday, addParentTaskToMonday, deleteTask } from './graph.js';

Now try to create, update, delete, and edit an item in the Bryntum Gantt chart. You’ll see the changes reflected in monday.com.

Next steps

This tutorial gives you a starting point for creating a Bryntum Gantt chart using vanilla JavaScript and syncing it with monday.com. There are many ways you can improve your Bryntum Gantt chart, for example, you can add features such as tree grouping. Take a look at our demos page to see demos of the available features.

Bryntum

Bryntum Gantt