Skip to main content

Few tips on Gantt Charts

Oracle APEX offers several beautiful chart types which are based on Oracle JET Data Visualizations. Gantt Chart is one such beautiful and useful chart. However, when I have searched in Google for some help on Gantt Charts, there are not many blogs posts talking about it. So, I thought, I could write one with few basic tips which I have learned this year.

I have used Gantt Chart to display employees calendar data and my targeted output was something like below.

Pic-1
Looks simple, correct? However, it's little tricky to get there.

Multiple Tasks Per Row:

When I look at the output, first I thought, I will have to write a query which gives tasks data and employee data with 1 row per task. That is, if there are 10 tasks to be displayed, then there should be 10 rows in SQL query output. But, it's not correct. Instead, I should have 1 row for each task + 1 row for each employee (parent). So I need to write a query which will output data in below format.

Pic-2

As we can see, there is 1 row per task and also there is 1 row per each employee (highlighted in blue color). Also, observe employee id EMP1 is used as PARENT_TASK_ID for EMP1 tasks.

Next, we need to do column mapping as follows. (Gantt Chart > Series > Column Mapping)

Pic-3
In Page Designer, we can read "Help" text for each option to get more info.

Next, define the timeline for which Gantt chart should be rendered. (Gantt Chart > Series > Timeline)
Pic-4

P6_START_DATE is "Start Date" field and P6_END_DATE is "End Date" field shown in Pic-1 page. P6_END_DATE_HIDDEN is hidden page item calculated based on P6_END_DATE field. We will discuss about this in "Date Handling" section.

By this step, we should have working Gantt chart ready.

Colors and Borders:

As we can see in Pic-1, I have used different colors and borders for different tasks, based on task type and other conditions. For this, I have added new column "CSS_CLASS" in series SQL query. This column gives CSS class(es) name for each task. Next, I need to specify this column name in "Task CSS Classes" section (Gantt Chart > Series > Appearance) as shown below.

Pic-5

Gantt Chart, like any other JET chart, is rendered as "Scalable Vector Graphics". So we have limited style options which we can use. We can use "fill" for "background-color" and "stroke" for "border".

We can put our CSS in "Page > CSS > Inline" section.

e.g. CSS styles

/* orangle fill */
path.demo-f-orange {fill:orange}

/* red border */
path.demo-b-red {
    stroke: red;
    stroke-width: 1px;
}

I found SVG and CSS tutorials useful on this topic. 

Dynamic Major, Minor Axes:

On top of the Gantt chart, we can see two axes. One Major Axis and other Minor Axis.

Pic-6
We can define these axes values under "Gantt Chart > Axes" Sections. If we want to display Gantt Chart for a fixed time window, then we can choose appropriate "Time Scale" and "Zoom Scale" for Major and Minor axes. However, if users can select the time window, then fixed axes may not look good in all cases.

Luckily, we can set these options using "JavaScript Initialization Code" (Gantt Chart > Attributes)

In my example, when user selects Start and End dates and submits page, then I have added page computation which will calculate days between start and end dates and saving the value to page item P6_GANTT_DAYS. 

Then, I have written below JS code in  "JavaScript Initialization Code" section.

function(options) {
  var windowDays = $v("P6_GANTT_DAYS");

  if (windowDays > 0 && windowDays <= 90) {
    options.minorAxis = {
      "scale": "days",
      "zoomOrder": ['quarters', 'months', 'weeks', 'days']
    };
    options.majorAxis = {
      "scale": "months",
      "zoomOrder": ['months', 'weeks', 'days']
    };
  } else if (windowDays > 90 && windowDays <= 270) {
    options.minorAxis = {
      "scale": "months",
      "zoomOrder": ['quarters', 'months', 'weeks', 'days']
    };
    options.majorAxis = {
      "scale": "quarters",
      "zoomOrder": ['quarters', 'months', 'weeks', 'days']
    };
  } else if (windowDays > 270 && windowDays <= 730) {
    options.minorAxis = {
      "scale": "months",
      "zoomOrder": ['quarters', 'months', 'weeks', 'days']
    };
    options.majorAxis = {
      "scale": "years",
      "zoomOrder": ['years', 'quarters', 'months', 'weeks', 'days']
    };
  } else {
    options.minorAxis = {
      "scale": "quarters",
      "zoomOrder": ['quarters', 'months', 'weeks', 'days']
    };
    options.majorAxis = {
      "scale": "years",
      "zoomOrder": ['years', 'quarters', 'months', 'weeks', 'days']
    };
  }

  return options;
}

With this, major and minor axes are set dynamically based on time window selected.

Date Handling:

When we try to render Gantt chart for two days, lets say 01-JAN-2020 as start date and 02-JAN-2020 as end date, then we would see only one day 01-JAN-2020 is displayed in Gantt chart. This is because, when we select date in date picker item, by default, time will be set as 0 hours. So chart will display only 01-JAN-2020. And if we try to display it for one day, by selecting same Start and End dates, then chart will not be rendered at all. To over come this problem, I have created another hidden field P6_END_DATE_HIDDEN. This field value is calculated based on P6_END_DATE (i.e. P6_END_DATE + 1 day)

Highlighting Today's Date:

We can highlight today's date in major or minor axis using JavaScript. Below code shows how we can highlight todays date in minor-axis. 
  • Create a Dynamic Action and give any proper name like "Highlight Todays Date"
  • In "When" Section, select
    • Event: After Refresh
    • Selection Type: Region
    • Region: Gantt Chart Region
  • In "True" section, choose "Execute JavaScript Code" action and put below JavaScript code in "Code" section.
var today = new Date();
// by default, dates in major, minor axis are displayed as MM/DD format
// change below code depending on how date format is being displayed
var todayGanttFormat = (today.getMonth() + 1) + "/" + today.getDate();
//console.log(todayGanttFormat);
// gantt-chart is region static id
$("text.oj-gantt-minor-axis-label", "#gantt-chart").each(function() {
  //console.log($(this).html());
  // here we are changing color of today's date to red
  if ($(this).html() == todayGanttFormat)
    $(this).css("fill", "red");
});

Tables Structure and Gantt Chart Query: 

Tables used for this demo and sample data.


Gantt Chart Series SQL Query:
-- employee records
SELECT
    'EMP' || emp.emp_id     task_id,
    NULL                    parent_task_id,
    emp.email_address       email_address,
    emp.email_address       task_name,
    NULL                    start_date,
    NULL                    end_date,
    'EMPLOYEE'              task_type,
    NULL                    css_class,
    NULL                    cust_tooltip
FROM
    gantt_emp emp
UNION ALL
-- employee tasks
SELECT
    'TK' || task.task_id            task_id,
    'EMP' || emp.emp_id             parent_task_id,
    emp.email_address               email_address,
    task.task_name                  task_name,
    task.start_date                 start_date,
    task.end_date + 1 - 1 / 1440    end_date,
    task.task_type,
    CASE
        WHEN task.task_type = 'LEAVE' THEN
                'demo-f-orange'
    END
    || ' '
    ||
    CASE
        WHEN task.billable_flag = 'N' THEN
                'demo-b-red'
        ELSE
            NULL
    END                             css_class,
    task.task_type                  cust_tooltip
FROM
         gantt_emp emp
    JOIN gantt_tasks task ON task.emp_id = emp.emp_id


APEX Versions: 18.2, 19.2

Thank you.

Other posts on this topic:

It's continuation of above blogpost where few more tips about Gantt Chart are discussed. Tips discussed are "Vertical Grid for Minor Axis" and "Dynamic Height for Gantt Chart".

This blog post explains how we can show custom tooltips on Gantt Chart Task bars.

This blog post provides a work around for implementing "Link" feature in Gantt Chart.

Comments

APEX Rocks said…
Very useful post on Gantt charts with example. This is exactly what I was looking for.

Great post! Thank you.
Reudiger said…
I do not find, where to create the "Task CSS Classes", as they a kind of LOV?
You wrote e.g. "path.demo-f-orange {fill:orange}" but how or where do I create path.demo-f-orange?
Is it a part of shared component?
Hari said…
Hi,

You don't need to create anything. Gantt chart task bars are rendered as "path". Gantt chart is SVG chart, it's not html. "path" is a SVG element.

Regards,
Reudiger said…
So, if I unterstand it right, my query has to deliver "path.demo-f-orange {fill:orange}" for a task, that's should be filled in orange?
Reudiger said…
Hello Hari, it is me again. I am still lost in coloring my tasks. May be You can "light my darkness".
I am still confused about the Task CSS classes.
Let say I have three different tasks: A,B and C. Now I createt a new Coloum as "CSS_CLASS" in series SQL query. What should be the result of this query part, if e.g. task A should be filled yellow, task B should be filled orange and task C should be filled red?
Regards, Ruediger
Hari said…
Hi Ruediger,

You can attach CSS classes to "Gantt chart task bars" from Gantt Chart SQL query.

e.g. SQL Query

SELECT
/* add columns required for gantt chart */
-- below column for dynamic styling task bars based on task data
,CASE
WHEN task_type = 'A' THEN
'css-class-a'
WHEN task_type = 'B' THEN
'css-class-b'
WHEN task_type = 'C' THEN
'css-class-c'
ELSE 'default-class'
END css_class
FROM your_table

Then you need to specify CSS Column name in "Task CSS Classes" using &css_class. syntax. Please refer "Colors and Borders" section in above blog post.

Next, you need to write CSS code for these classes. You can write CSS code in "Page Properties > CSS > Inline" section.

e.g. CSS code below:

/* css-class-a */
path.css-class-a {fill:orange}

/* css-class-b */
path.css-class-b {
stroke: red;
stroke-width: 1px;
}

Hope it helps.

Reudiger said…
Yes, it helped. Now I got the missing point.
Thx a lot, Ruediger
Jon D said…
Excellent post. Thank You!
Bharat said…
Great Post. Have you tried adding grid line for current date? any suggestion is it possible to achieve?
Coach Dave said…
So so happy I found this post. Thank you for putting it together.
Hari said…
Thank you. There are couple of other blog posts on this topic. You can find them here https://srihariravva.blogspot.com/p/table-of-contents.html.
Hari said…
Hi Bharat,

I am not aware of any technique using which we can specify grid line only for current date. If you are looking for grid lines for all days, then you can achieve it by swapping the axes. Please refer "Few Tips on Gantt Charts - Part 2" post for more info on this.

If you want to highlight current date somehow, then we can do this using JavaScript. I have updated this blog post with example JavaScript code and demo page has been updated.
Unknown said…
Hello, thank you for all this stuff. i have two questions. The first is how could we get the gantt chart to display multitasks ordered in lines regarding their names? i mean with your example employee1 for instance, we can see weekend in black color and others tasks orange and blue, we observe that we can have weekends on first line and on other lines, everything can be melt. hope i am clear. for me, as i got over 6 differents tasks by rowname, it's confusing to have the beginning of first task on row1 and to have the continuation of this task (same name but on date next) on row 6 for example...is there a way to render taskname by row? second question for me less important, is there a way to have a vertical line (red) for present day? thank you
Unknown said…
Hello Hari, thank you for posting on present day display. however i encounter difficulties with it. i uncomment the console log to understand and see that when i choose days for minor axis: like this in javascipt initialization code options.majorAxis = {"scale":"months"};
options.minorAxis = {"scale":"days"}; i don't see anydays, if i try weeks or months i can see them in the console. therefore it does not match the todayGanttFormat variable and no red line displayed. have you got an idea what is wrong? by the way how could i set the format date dd/mm instead of mm/dd? thank you
Bharat said…
Thanks Hari. It is useful as well.

For Grid line I tried below js with defining css.

var d = new Date();
var n = d.getDate();

n = n -1

$(".oj-gantt-major-axis-label")[n].attributes[7].value="oj-gantt-major-axis-label newcss"

$(".oj-gantt-vertical-gridline")[n].attributes[5].value="oj-gantt-major-axis-label newcssgrid"
Kumar said…
Hi Hari,

Excellent post. Is it possible to change the shape of the process bar on gantt chart? Because if the task start and end dates are same then process bar on the chart is being shown as a milestone (diamond shape).

Thank you
Hari said…
Hi Kumar,

Yes, you are correct. When start and end dates are same, then the task is displayed as diamond shape (Milestone).

AFAIK, it is not possible to change the shape of tasks bar in Gantt chart. You can explore Oracle JET documentation. Depending on APEX version (and underlying JET version), we can use features available in JET.

Kumar said…
Thanks for your input Hari. I will check.
Unknown said…
Hey Hari, I have a question to regarding the Gantt Charts in Apex. In my application I need the datasets (the tasks) to hold a bit more information, something like an ordernumber or a location etc. I do load that in my SQL query, but I do not select them in the Apex dropdown menus (where Task ID, Task Name etc is located).So once the Chart is generated, it seems like I can't access this information anymore, to use it for Linking to different pages or something similar.

Do you know if there is any solution to my problem?
Hari said…
Hi, You can use tooltips to show additional information about tasks. Please refer Custom Tooltip blogpost. For defining link on the task bar, please refer blogpost.
Hari said…
For defining link on the task bar, please refer blogpost.
George said…
Hello Hari!

Your post is excellent and it's really great to see an article about this subject.
However, I'm not sure how I can recreate the same output of the query you have as source for this chart. In my case, I have the same structure of the table, but I don't have any clue about how to get the parent row for each employee. Can you provide please the code for this query output?

Thanks and have a nice day!
Hari said…
Hi George,

Thanks for your feedback. I am glad you find it helpful.

I have updated the blogpost and added details about tables used for this demo. I have also added Gantt chart query. I am using UNION to display employee data and tasks data in single query.



Dave L said…
Hari, you are today's Hero of the Day!
There's a distinct lack of documentation, and this blog post answered all of my questions.
Many thanks,

Dave
Ken_B said…
Is there a way to change the background color of weekend days?

Popular posts from this blog

Interactive Grid - Conditional Enable/Disable

In this blogpost, I am going to discuss few approaches using which we can conditionally enable/disable Interactive Grid (IG) column(s) based on other column(s) values. Note:    There is a bug  30801170  in APEX 19.2/20.1 with respect to "enable/disable" dynamic actions for IG columns. Workaround for this bug is provided at the end of this blogpost . This bug has been fixed in APEX version 20.2. Client Side Only Conditions If conditions to enable/disable are simple, then we can check those conditions easily on the client side. For e.g. let's consider IG on EMP table with following SQL Query. SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO FROM EMP Let's consider the requirement as - Enable "Commission" column only when JOB is equals to 'SALESMAN' and disable "Commission" column in all other cases. This can be done declaratively using dynamic actions (DA) DA "Enable/Disable Commission - 1" Create DA and give it a prope...

Interactive Grid - Bulk Operation on Selected Rows

What's the Problem? Let's say you have an IG on employee table and you want to update selected employees commission based on user's input. Ideally, it should be very simple, where you can write UPDATE statement in page process and select IG region as "Editable Region" under "Execution Options" of the page process. But, when you select rows and submit page, you can see that, this process won't get executed! The reason is  Selection of 'Row Selector' check-boxes is not considered as row-change. Thus selected rows are not submitted to server. So, is there any work around?  Yes! Luckily there are lot of JavaScript (JS) APIs available to work with IG. If you are not already aware, you can refer "APEX IG Cookbook"  or  JavaScript API Reference documentation. If we continue with above Employee IG example, when user selects IG rows, enters "Commission %" and clicks on "Update Commission" button, then we can writ...

Interactive Grid - Process Filtered Data on Server Side

Recently one of the APEX developers has reached out to me and asked if it's possible to capture filtered rows data of the Interactive Grid on the server-side and do some processing. In APEX 20.1, there is a new API APEX_IG , using which we can achieve this. Photo by Jakub Kapusnak on Unsplash The approach is very simple and straightforward. Get the internal region id based on the Static ID given for the IG region Get the last viewed report id based on region id Open query context for the region and report using region id and report id Fetch and loop through the rows using the query context Do something with fetched rows And finally, close the query context If you have already done this for interactive reports, then you should be already aware of these steps. The only difference here is, we use APEX_IG APIs instead of APEX_IR APIs. For the demo purpose, let's Build an Interactive Grid on EMP table and let's give it a Static ID as emp Create a Textarea page item with...