This is the 2nd part of my series of articles around Gantt Charts in XPages. Today, we want to write changes back to Domino, so if we move Blocks around, we want to send these changes back to Domino.

To do this, we need to refactor our sample from the first article to some extend - especially the FETCH Call needs to be done differently. To streamline things, we will go back a couple of years and use an XMLHttpRequest instead of FETCH.

In this first sample, we will focus on how to send back date changes from the Chart back to the NSF - so if we move the start and end dates of a Task, we want to write the changes back to Domino. We will do this by using a 2nd REST call from the extension library which is the "documentJSONService", which will allow us to interact on documents by accessing them vie their UNID in the url like so:

A GET-Request to "https://..../ganttest.nsf/tasks.xsp/savetask/unid/7736007ED4EC9492C1258836004DDB9D" will send back the document data:

{"@unid":"7736007ED4EC9492C1258836004DDB9D","@noteid":"38F6","@created":"2022-05-02T14:10:24Z","@modified":"2022-05-10T07:59:40Z","@authors":"CN=Heiko Voigt\/O=SIT GmbH\/C=DE","@form":"Task","$UpdatedBy":"CN=Heiko Voigt\/O=SIT GmbH\/C=DE","$Revisions":["2022-05-02T14:10:52Z","2022-05-02T14:54:32Z","2022-05-02T14:57:07Z","2022-05-02T15:02:09Z","2022-05-02T15:03:23Z","2022-05-02T15:04:27Z","2022-05-09T16:34:19Z","2022-05-09T17:45:51Z","2022-05-10T06:58:40Z","2022-05-10T07:00:07Z","2022-05-10T07:11:01Z","2022-05-10T07:11:54Z","2022-05-10T07:12:21Z","2022-05-10T07:39:25Z","2022-05-10T07:39:36Z","2022-05-10T07:40:43Z","2022-05-10T07:59:18Z"],"start":"2022-05-01","name":"Overall Task","end":"2022-05-30","id":"HVOT-36663","sort":"0"}

In our case, we will make use of the PATCH-Request, to change certain fields in the dataset.

But first, let's define our new REST Service as a 2nd Service in our Service Xpage - in my case this is the page called tasks.xsp:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
	//** REST Service for Tasklist **/

	<xe:restService id="restJSONService" jsId="getViewData"	pathInfo="taskList">
		<xe:this.service>
			<xe:viewJsonService defaultColumns="true"
				keysExactMatch="true" var="dataRow" viewName="lu4IDs">
			</xe:viewJsonService>
		</xe:this.service>
	</xe:restService>

	//* 2nd Service for Document handling */

	<xe:restService id="restService1" jsId="saveTask" pathInfo="savetask">
		<xe:this.service>
			<xe:documentJsonService compact="true"
				computeWithForm="false" contentType="application/json"
				defaultItems="true" formName="Task">
			</xe:documentJsonService>
		</xe:this.service>
	</xe:restService>

</xp:view>

We will use the pathinfo "savetask" to access this service. I do not use the "ComputeWithForm" option as this would result in new internal id's everytime we save a task.

Now, let's continue on to our Xpage that renders the Gantt Chart. As described above, we need to re-factor our data calls a bit. To streamline this, I decided to add a new Client Side JavaScript Library to the project to consolidate the data handling there. I named this new library "GanttFeatures.js" and added it to the resources of my rendering page like so:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
	xmlns:xe="http://www.ibm.com/xsp/coreex">
	<xp:this.resources>
		<xp:headTag tagName="script">
			<xp:this.attributes>
				<xp:parameter name="type" value="text/javascript" />
				<xp:parameter name="src" value="dist/frappe-gantt.js" />
			</xp:this.attributes>
		</xp:headTag>
		<xp:styleSheet href="/gantt_sit.css"></xp:styleSheet>
		<xp:styleSheet href="/frappe-gantt.css"></xp:styleSheet>
		
		<xp:script src="/GanttFeatures.js" clientSide="true"></xp:script>
	</xp:this.resources>

Inside of this library are three functions. One defines a basic httpRequest Object like so:

function create_request() {
         var httpRequest;
         if (window.XMLHttpRequest) {
            httpRequest = new XMLHttpRequest();
         }
         else if (window.ActiveXObject) { 
            httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
         }
         else
         {
            document.write("Unable to create XMLHttpRequest object.");
         }

         return httpRequest;
      }

We will be using this in our HTTP Requests to get our data:

function returnTasks() {
	
	var httpRequest = create_request();

    httpRequest.open("GET", "https://..../ganttest.nsf/tasks.xsp/taskList", false);
    httpRequest.send(null);
    if (httpRequest.status == 200) // For http requests.
    {
       return JSON.parse(httpRequest.responseText);
    }
    else
    {
       return JSON.parse("{}");
    }
}

This changes our asynchronous call from the first sample using FETCH to a synchronous call.

To write back the changes of the Date Fields from the Gantt Chart, we add another function to the library to do the necessary PATCH call:

function saveDateChange(task, start, end) {
	var httpRequest = create_request();
	httpRequest.open("PATCH","https://.../ganttest.nsf/tasks.xsp/savetask/unid/"+task["@unid"]);
	httpRequest.setRequestHeader("Content-Type", "application/json; charset=utf-8");
	httpRequest.setRequestHeader( "X-HTTP-Method-Override","PATCH");
	var body=(JSON.stringify({"start":start,"end":end}));
	httpRequest.send(body);
	if (httpRequest.status == 200) // For http requests.
    {
       return({"Status":"OK"});
    }
    else
    {
       return ({"Status":"Error"});
    }
}

This function will take the changed task object as well as the new start and end date.

With these functions in place, we can change our output Scriptblock in the rendering XPage:

<xp:scriptBlock id="scriptBlock1">

		<xp:this.value><![CDATA[
var tasks = returnTasks();
console.log("Test",tasks);
var gantt_chart = new Gantt(".gantt-target", tasks, {
	on_click: function (task) {
		console.log(task);
	},
	on_date_change: function(task, start, end) {
		console.log(task, start, end);
		var back = saveDateChange(task, start, end);
	},
	on_progress_change: function(task, progress) {
		console.log(task, progress);
	},
	on_view_change: function(mode) {
		console.log(mode);
	},
	on_click: function (task) {
		console.log(task);
	},
	header_height: 50,
	column_width: 30,
  	step: 24,
	view_modes: ['Quarter Day', 'Half Day', 'Day', 'Week', 'Month'],
	bar_height: 20,
  	bar_corner_radius: 3,
  	arrow_curve: 5,
  	padding: 18,
  	view_mode: 'Day',
   	date_format: 'YYYY-MM-DD',
   	custom_popup_html: null,
	language: 'en'
});

	]]></xp:this.value>
	</xp:scriptBlock>

Now, we read our tasks by calling the returnTasks() function. In the Event Handler for on_date_change, we call our saveDateChange() function and hand over the respective objects.

Now, you can move tasks around. If you move a task with dependencies, all of the dependent tasks will be moved as well and the changes will be reflected in Domino. VoilĂ  !

Next up - Integrate a button bar to switch views (day/week/month).

Happy codeing.

Heiko.