Our state of the art Gantt chart


Post by Dolan »

I'm having issues with the Gantt chart throwing errors when I create tasks.

This is the code I am using to initalise the gantt chart

project = new ProjectModel({
  transport: {
    load: {
      // url: 'https://localhost:3001/static/test-datasets/launch-sass-min.json'
      url: `${apiFactory.getBaseURL()}gantt/${User.projects[selectedProject].id}/`,
      credentials: 'omit',
      requestConfig: {
        headers: {
          Authorization: `Token ${Token.token_string}`,
          'X-Company': Company.getSelectedCompanyId(),
        },
      },
    },
    sync: {
      url: `${apiFactory.getBaseURL()}gantt/${User.projects[selectedProject].id}/`,
      credentials: 'omit',
      requestConfig: {
        headers: {
          Authorization: `Token ${Token.token_string}`,
          'X-Company': Company.getSelectedCompanyId(),
        },
      },
    },
  },

  autoSync: true,
});

gantt = new Gantt({
  adopt: 'gantt',

  project: project,
  startDate: User.projects[selectedProject].start_date,
  endDate: User.projects[selectedProject].complete_date,

  rowHeight: 40,
  barMargin: 7,

  width: 'calc(100vw - 420px)',

  columns: [{ type: 'name', field: 'name', width: 250 }],

  features: {
    showDirty: false,

taskContextMenu: {
  // We would like to remove some of the provided options in the add menu
  items: {
    add: {
      menu: {
        items: {
          subtask: false,
          successor: false,
          predecessor: false
        }
      }
    },
    indent: false,
    outdent: false
  },
},

taskEdit: {
  tabsConfig: {
    // change title of Dependencies tab
    // predecessorstab: {
    // 	title: 'Dependencies',
    // },
    predecessorstab: false,
    successorstab: false,

    // remove Notes tab
    notestab: false,
    // remove Resources tab
    resourcestab: false,
    // remove Advanced tab
    advancedtab: false,
  },
},
  },

  // Custom task content, display task name on child tasks
  // taskRenderer({ taskRecord }) {
  // 	if (taskRecord.isLeaf && !taskRecord.isMilestone) {
  // 		return taskRecord.name;
  // 	}
  // },
});

project.load();

The initial data that gets sent back from the server is as follows

{
  "success": true,
  "tasks": {
    "rows": [
      {
        "id": "ad2fe597-ec7c-41d3-a713-849f531224cc",
        "createdAt": "2020-09-21T03:02:59.824235Z",
        "updatedAt": "2020-09-21T03:02:59.824235Z",
        "percentDone": 0,
        "constraintType": "startnoearlierthan",
        "constraintDate": "2020-08-12T14:00:00Z",
        "effortDriven": false,
        "schedulingMode": "Normal",
        "effort": 24.0,
        "effortUnit": "hour",
        "manuallyScheduled": false,
        "startDate": "2020-08-12T14:00:00Z",
        "endDate": "2020-08-17T14:00:00Z",
        "duration": 5.0,
        "durationUnit": "day",
        "name": "New task 1",
        "$PhantomId": "_generated_0x2dd2af1",
        "dependencies": []
      },
      {
        "id": "529e8584-9662-4cd8-979b-2ddb04d5fcb8",
        "createdAt": "2020-09-20T23:58:53.293328Z",
        "updatedAt": "2020-09-21T03:02:59.826232Z",
        "percentDone": 0,
        "constraintType": "startnoearlierthan",
        "constraintDate": "2020-08-19T14:00:00Z",
        "effortDriven": false,
        "schedulingMode": "Normal",
        "effort": 24.0,
        "effortUnit": "hour",
        "manuallyScheduled": false,
        "startDate": "2020-08-19T14:00:00Z",
        "endDate": "2020-08-19T14:00:00Z",
        "duration": 0.0,
        "durationUnit": "day",
        "name": "New milestone 1",
        "$PhantomId": "_generated_0x2dd2af1",
        "dependencies": []
      },
      {
        "id": "486cee2d-2047-45d8-afaf-54721b336c6e",
        "createdAt": "2020-09-20T23:57:11.388334Z",
        "updatedAt": "2020-09-21T03:03:05.494084Z",
        "percentDone": 0,
        "constraintType": "startnoearlierthan",
        "constraintDate": "2020-08-11T14:00:00Z",
        "effortDriven": false,
        "schedulingMode": "Normal",
        "effort": 24.0,
        "effortUnit": "hour",
        "manuallyScheduled": false,
        "startDate": "2020-08-12T14:00:00Z",
        "endDate": "2020-08-17T14:00:00Z",
        "duration": 5.0,
        "durationUnit": "day",
        "name": "Test",
        "$PhantomId": "_generated_0x2dd2af1",
        "dependencies": []
      }
    ]
  },
  "dependencies": { "rows": [] }
}

Then after you create a new task the ProjectModel posts the new task
Request

{
  "type": "sync",
  "requestId": 16006580210072,
  "tasks": {
    "added": [
      {
        "cls": "",
        "baselines": [],
        "percentDone": 0,
        "parentId": null,
        "parentIndex": 3,
        "effortDriven": false,
        "schedulingMode": "Normal",
        "effort": 24,
        "effortUnit": "hour",
        "manuallyScheduled": false,
        "startDate": "2020-08-12T14:00:00.000Z",
        "endDate": "2020-08-13T14:00:00.000Z",
        "duration": 1,
        "durationUnit": "day",
        "direction": "Forward",
        "calendar": "_generated_0x2dd2af1",
        "name": "New task",
        "$PhantomId": "_generated_0x2dd2af1"
      }
    ]
  }
}

Response

{
  "success": true,
  "requestId": 16006580210072,
  "tasks": {
    "added": [
      {
        "id": "bcad7e62-b884-4ea6-8c1f-b657ba04d362",
        "createdAt": "2020-09-21T03:13:41.105942Z",
        "updatedAt": "2020-09-21T03:13:41.106441Z",
        "percentDone": 0,
        "constraintType": "startnoearlierthan",
        "constraintDate": "2020-09-21T03:13:41.105942Z",
        "effortDriven": false,
        "schedulingMode": "Normal",
        "effort": 24.0,
        "effortUnit": "hour",
        "manuallyScheduled": false,
        "startDate": "2020-08-12T14:00:00.000Z",
        "endDate": "2020-08-13T14:00:00.000Z",
        "duration": 1.0,
        "durationUnit": "day",
        "name": "New task",
        "$PhantomId": "_generated_0x2dd2af1",
        "dependencies": []
      }
    ],
    "updated": [],
    "removed": []
  },
  "dependencies": { "added": [] }
}

I'm not sure why but after this, whenever I try to move the new task left and right it throws this error in the browser console and I don't know why.

Uncaught TypeError: _0xce0fc6 is undefined
    selectEvent gantt.module.js:190
    processEvent gantt.module.js:190
    onTargetClick gantt.module.js:28
    _0x4fc3c0 gantt.module.js:10
    addElementListener gantt.module.js:10
    on gantt.module.js:10
    set target gantt.module.js:28
    set gantt.module.js:10
    setConfig gantt.module.js:10
    configure gantt.module.js:10
    construct gantt.module.js:10
    construct gantt.module.js:10
    Base gantt.module.js:10
    Events gantt.module.js:10
    Navigator gantt.module.js:28
    construct gantt.module.js:177
    construct gantt.module.js:190
    Base gantt.module.js:10
    Localizable gantt.module.js:10
    Events gantt.module.js:10
    Delayable gantt.module.js:10
    Widget gantt.module.js:10
    Container gantt.module.js:10
    Pluggable gantt.module.js:22
    State gantt.module.js:22
    GridElementEvents gantt.module.js:138
    GridFeatures gantt.module.js:138
    GridNavigation gantt.module.js:138
    GridResponsive gantt.module.js:138
    GridSelection gantt.module.js:138
    GridState gantt.module.js:138
    GridSubGrids gantt.module.js:153
    GridBase gantt.module.js:153
    TimelineDateMapper gantt.module.js:177
    TimelineDomEvents gantt.module.js:177
    TimelineEventRendering gantt.module.js:177
    TimelineScroll gantt.module.js:177
    TimelineViewPresets gantt.module.js:177
    TimelineZoomable gantt.module.js:177
    TimelineBase gantt.module.js:177
    CrudManagerView gantt.module.js:162
    GanttDom gantt.module.js:190
    GanttRegions gantt.module.js:190
    GanttScroll gantt.module.js:190
    GanttState gantt.module.js:190
    GanttStores gantt.module.js:190
    GanttTimelineDateMapper gantt.module.js:190
    Delayable gantt.module.js:10
    EventNavigation gantt.module.js:177
    TaskNavigation gantt.module.js:190
    GanttBase gantt.module.js:190
    Gantt gantt.module.js:190
    initGanttChart gantt.js:209
    onchange gantt.js:876
    callback mithril.js:948

I have a feeling that the response from the server is somehow messing with the data parser in the ProjectModel, but I'm not sure.

I really appreciate any help given, thanks!


Post by arcady »

The response is incorrect. Please check this guide: https://bryntum.com/docs/gantt/#guides/crud_manager.md

Response property names do not 100% match request ones added/updated records info should be replied in rows

{
  "success": true,
  ...
  "tasks": {
    "rows": [
      {

Next thing that worth mentioning is server should return only field values that are "new" to the client (well except record identifiers). So the client would update corresponding record fields with for example values built on the server ..or values provided in another parallel session.

In case of an added record as bare minimum the server should return the record $PhantomId - phantom identifier (assigned to the record by the client) and id - real identifier (set to the record by the database).
So in your case it should've looked like this:

{
  "success": true,
  "requestId": 16006580210072,
  "tasks": {
    "rows": [
      {
        "id": "bcad7e62-b884-4ea6-8c1f-b657ba04d362",
        "createdAt": "2020-09-21T03:13:41.105942Z",
        "updatedAt": "2020-09-21T03:13:41.106441Z",
        "$PhantomId": "_generated_0x2dd2af1"
      }
    ]
  },
  ...
}

I've intentionally kept createdAt and updatedAt fields since they look like values built on the server so they should be responded then.


Post by Dolan »

Thanks for the quick response!

I've changed the 'tasks.added' response to 'tasks.rows' and that has fixed my errors. I tried returning both the updated and added in 'tasks.rows' but that threw errors for me, so keeping them separate must be the way to go.

Thanks so much for the help!

Also, just 2 quick questions, can I disable dependencies, these dots that you drag to other tasks.

And also can you change what format the dates are in up the top of the Gantt view?

Image


Post by arcady »

Dolan wrote: Tue Sep 22, 2020 1:58 am

Also, just 2 quick questions, can I disable dependencies, these dots that you drag to other tasks.

This can be done by configuring dependencies feature of the Gantt.
It has allowCreate config which enables/disables creation of dependencies w/ drag'n'drop:

const gantt = new Gantt({
    ...

    features : {
        dependencies   : {
            allowCreate : false
        },
       ...
Dolan wrote: Tue Sep 22, 2020 1:58 am

And also can you change what format the dates are in up the top of the Gantt view?

Yes that's doable but needs explanation. The time axis can zoom (from years to month ..then to weeks to days etc..) and its column headers naturally depend on the current zoom level.
Zoom levels are registered in PresetManager. Each zoom level is represented with a ViewPreset instance.
So when you want to change date formats in the headers you basically want to change date format of the corresponding zoom level.
To know current zoom level please use:

gantt.zoomLevel

You can inspect a zoom level definition in your browser console:

gantt.presets.records[9].data

In that particular case you want to change formats which are defined in headers array

gantt.presets.records[9].data.headers

So here is for example I replace formats for zoom level #9 (I've copypasted its headers array and changed dateFormat):

const
    // get default zoom presets
    presets   = PresetManager.records,
    // making a new view preset
    newPreset = PresetManager.createRecord(
        {
            // use preset of zoom level #9 as basis
            base           : presets[9].id,
            // but override headers definition
            headers : [
                {
                    increment           : 1,
                    unit                : 'week',
                    dateFormat          : 'YYYY-MM-DD',
                    verticalColumnWidth : 115
                },
                {
                    increment           : 1,
                    unit                : 'day',
                    dateFormat          : 'DD',
                    verticalColumnWidth : 25
                }
            ]
        }
    );

// replace default zoom level #9 with our custom one
presets.splice(9, 1, newPreset);

const gantt = new Gantt({
    // provide our customized zoom presets
    presets,
    ...

Or as an option you can add a new [url=https://www.bryntum.com/docs/gantt/#Scheduler/preset/ViewPreset]ViewPreset[/url]. This is demonstrated in [url=https://www.bryntum.com/examples/scheduler/configuration/]this demo[/url].

Post by Dolan »

Awesome! That all works perfectly. Thanks for your help arcady!


Post Reply