Our state of the art Gantt chart


Post by Jerther »

Gantt.feature.TaskEdit#editorConfig is a configuration for Gantt.widget.TaskEditor. So that is covered.

Does not exactly apply here. The guide explains to move the extra widgets from features.taskEdit.editorConfig.extraItems to features.taskEdit.items. That means I must move the configuration from inside my custom TaskEditor class out to the taskEdit feature configuration. Right? If it is so, if it is really really so, it breaks my code but I'm fine with that.

defaultConfig was never public.

Does that mean I shouldn't use it? I can see it's "internal" but so is configurable() and the latter is demonstrated in https://www.bryntum.com/docs/gantt/#Core/guides/advanced/widgets.md#the-widget-class so I'm not sure what the quick tip "This member should not be used by non-framework code" means exactly. My understanding is that private stuff is just not shown in the documentation.

"defaultConfig" is getting replaced with "configurable".

Thanks for the heads up. I will stop using it.

Anyway, I carefully read https://www.bryntum.com/docs/gantt/#Core/Base#property-configurable-static **. The example is clear, and works very well: the configurations of the Example and Example2 classes are merged, and this is what I expect. Unless you use let ex = new Example2(); then ex.config becomes { bar: {} }. Strange. let ex = new Example2({config: {}}); is the least that can be used to get any expected result. But I digress ;)

So I did some more tests and here's where it gets interresting: it merges fine, UNLESS there's an array. Then that array gets completely replaced:

class Example extends Base {
     static get configurable() {
         return {
             config : {
                 foo : 1,
                 bar : [{
                     ref: 'first',
                     adjo: 'abc'
                 }, {
                     ref: 'second',
                     adjo: 'def'
                 }]
             }
         };
     }
 }


 class Example2 extends Example {
     static get configurable() {
         return {
             config : {
                 bar : [{
                     ref: 'second',
                     adjo: 'ghi'
                 }, {
                     ref: 'third',
                     adjo: 'jkl'
                 }],
                 zip : 'abc'
             }
         };
     }
 }

let ex = new Example2({config: { zip: 'xyz'}});
console.debug(ex.config);

// Expected result:
{
	"bar": [{
		"ref": "first",
		"adjo": "abc"
	}, {
		"ref": "second",
		"adjo": "ghi"  // This one is updated
	}, {
		"ref": "third",
		"adjo": "jkl"
	}],
	"foo": 1,
	"zip": "xyz"
}

// Expected result if ref was not to be handled:
{
	"bar": [{
		"ref": "first",
		"adjo": "abc"
	}, {
		"ref": "second",
		"adjo": "def"
	}, {
		"ref": "second",
		"adjo": "ghi"
	}, {
		"ref": "third",
		"adjo": "jkl"
	}],
	"foo": 1,
	"zip": "xyz"
}

// Actual result:
{
	"bar": [{  // MISSING ONE!
		"ref": "second",
		"adjo": "ghi"
	}, {
		"ref": "third",
		"adjo": "jkl"
	}],
	"foo": 1,
	"zip": "xyz"
}

And that is what I'm experiencing. https://www.bryntum.com/docs/gantt/#Gantt/widget/TaskEditor#config-items has to be an array (see my original post) so it gets completely replaced. If I could refer to "tabs" with items: { tabs: {...} } instead of items: [ { ref: 'tabs', ...} ]then I believe the matter here would be over.

I'm sorry I don't mean to challenge you guys. What I did in 2.1.7 worked but maybe it was just wrong. I don't know ;) The right strategy to use is a bit confusing and I just need to get it clear.

** There'a a typo: To support this, config options ca be declared like
** The result reads ex.foo = { but should be ex.config = {


Post by Jerther »

OK I've dug a little deeper, (well, mabe more than a little) and I could come with something that works. First, let me enumerate and recap a few things about the GanttTaskEditor configuration:

1- The real items configuration is in GanttTaskEditor so I should really extend it instead of TaskEditor.
2- The items configuration comes from the defaultConfig property, not configurable.
3- The merging of config elements works well until an element is an array, then it's overwritten.
4- https://www.bryntum.com/docs/gantt/#SchedulerPro/widget/GanttTaskEditor#config-items says items can be an object, but it causes an error. Some code uses the find() method on items to find the item with ref set to "tabs" but of course: TypeError: clonedItems.find is not a function . See my first post for details.
5- So because of #4, the items config must be an array. Then see #3.

I've come up with this workaround. Load the basic demo and just replace the whole code with the following:

import { Gantt, ProjectModel, GanttTaskEditor, Config } from '../../build/gantt.module.js?450897';
import shared from '../_shared/shared.module.js?450897';
/* eslint-disable no-unused-vars */


class MyTaskEditor extends GanttTaskEditor {
    static get type() {
        return 'mytaskeditor';
    }
    static get defaultConfig() {
        const myConfig = {
            autoClose: false,
            width: '50em',
            height: '36em',
            items: super.defaultConfig.items // This is an array so it will overwrite anything from the base class. So start with array from base class, and merge my own stuff into it next
        };
        const myTabs = {
            generalTab: {
                items: {
                    spacer1: {
                        html    : '',
                        cls  : 'b-divider',
                        flex : '1 0 100%'
                    },
                    tags: {
                        type  : 'combo',
                        ref   : 'tagsField',
                        label : "Tags",
                        flex  : '0 100%',
                        cls   : 'b-inline'
                    }
                }
            },
            advancedTab: {
                items: {
                    initiallyExpanded: {
                        type: 'checkbox',
                        label: "Initially expanded",
                        ref: 'initiallyExpandedField',
                        flex: '1 0 50%',
                    }
                }
            },
        };
        const tabs = myConfig.items.find(i => i.ref == 'tabs');
        tabs.items = Config.merge(myTabs, tabs.items);
        return myConfig;
    }
}

MyTaskEditor.initClass();


const project = new ProjectModel({
    transport : {
        load : {
            url : '../_datasets/launch-saas.json'
        }
    }
});

new Gantt({
    appendTo : 'container',

project,

features : {
    taskEdit : {
        editorClass: MyTaskEditor
    }
},

});

project.load();

SO! All this leads me to believe there's a bug, either in:
A- https://www.bryntum.com/docs/gantt/#SchedulerPro/widget/GanttTaskEditor#config-items which says items can be an object.
B- the code in in TaskEditorBase.changeItems() that uses find() on items to find the "tabs" item without checking if items is an Array or an Object first. (Although I tried to fix that line but got errors elsewhere.)
C- The amount of effort I'm putting into this trying to save my old code ;)

Phew! :)


Post by saki »

Hello Jerther,

I do understand your position and efforts. I think that extending GanttTaskEditor should be fully supported without any problems and it should also accept object as items. The documentation is inherited from Container but the task editor does not fully implement support for items as an object.

What could be done here would be to implement your own changeItems method, but it is more theoretical possibility, not an advise because the method is not public.

Regarding overwriting arrays used as config options, this is true also for scalar values as described here: https://www.bryntum.com/docs/gantt/#Core/guides/advanced/widgets.md#inheritance-and-compose() In other words, config options are merged (if they have different names) but not their children – we could call it a "shallow merge."

I have created a Feature Request here: https://github.com/bryntum/support/issues/3080

Until it will be implemented, use please a solution that works for you, possibly with minimum private methods or properties used.


Post by Jerther »

What could be done here would be to implement your own changeItems method

And then again, it fails elsewhere :(

use please a solution that works for you, possibly with minimum private methods or properties used.

So for now I'll stick with the workaround I came up with in my last post. It uses all documented stuff (although internal?) and it allows me to reuse my 2.1.7 code without any major change.

Thanks Saki. I appreciate your consideration, and I'll follow the ticket.


Post Reply