Our pure JavaScript Scheduler component


Post by Rama »

Dear Support,

I have added a custom field in following way inside beforeEventEditShow in following way

 const mapOfIdAndText = customFieldValues.map(r => {
            return  {id: r.id, text: r.state}
        });

    const field = WidgetHelper.createWidget({
        type:'combobox', 
        label: 'My field',
        id: "MyField",
        items: mapOfIdAndText
    });
    
    event.editor.insert(field, 1);

It shows up as well

CustomField.png
CustomField.png (17.76 KiB) Viewed 2075 times

Now inside beforeEventAdd method - following is what I get in eventRecord

{
	"startDate": "2020-06-02T02:00:00.000Z",
	"endDate": "2020-06-02T03:00:00.000Z",
	"durationUnit": "d",
	"cls": "",
	"draggable": true,
	"resizable": true,
	"id": "_generated_0x14e7473",
	"duration": 0.041666666666666664,
	"name": "Test",
	"resourceId": "abc"
}

How do I get value that user selected for custom field I added to Editor popup.


Post by mats »

Did you add your custom field to a Model subclass and provide it to your EventStore? See docs here for how to create a subclass:

https://bryntum.com/docs/scheduler/#Core/data/Model

and

https://bryntum.com/docs/scheduler/#Core/data/Store#config-modelClass


Post by Rama »

Dear Support,

I am just loading data from server and binding scheduler with the data. I am using "beforeEventAdd" event to invoke an api and save the new event in backend.

In my configuration of Scheduler, I have not defined any store. Can you point me to some example in React of how to define the store and pass it to scheduler.

This is how I have defined scheduler

<BryntumScheduler
                    // Make grid grow to fit rows
                    height="650px"
                    width="100%"
                    ref={scheduler}
                    // Initial row height
                    barMargin={schedulerData.barMargin}
                    eventsVersion={schedulerData.eventsVersion}
                    resourcesVersion={schedulerData.resourcesVersion}
                    events={schedulerData.events}
                    resources={schedulerData.resources}
                    timeRanges={{
                        store: {
                            modelClass: TimeSpan,
                            storeId: 'timeRanges',
                            data: schedulerData.timeRanges
                        }
                    }}
                    treeFeature={true}
                    regionResize={true}
                    monitorResize={true}

                listeners={{
                    eventclick: onEventClick,
                    toggleNode: onToggleNode,
                    beforeEventAdd: onBeforeEventAdd,
                    beforeEventDelete: onBeforeEventDelete,
                    eventResizeEnd: onResizeEventEnd,
                    eventEdit: onEventEdit,
                    afterEventSave: onAfterEventSave
                }}

                startDate={startDate}
                endDate={endDate}

                // Columns in scheduler
                columns={[
                    {
                        type: 'tree',
                        text: 'Project',
                        showImage: false,
                        width: 130,
                        field: "name",
                    }
                ]}
                eventRenderer={eventRenderer}

            />

Post by pmiklashevich »

Hello,

We have a simple example called Event Editor with Cascading combos. You can see how it's implemented in Scheduler/examples/eventeditor-combos/app.js

Here are few steps to make saving work:

  1. Define your model fields. autoExposeFields is enabled, but to make it work need to have these fields defined on the first record. So cannot rely on this.

    class Task extends EventModel {
        static get fields() {
            return ['MyField'];
        }
    }
    
  2. Need to set your new model class to be used in Event Store. Instead of events need to specify eventStore and set modelClass and data:

            eventStore : {
                // specify model class
                modelClass : Task,
                data : tasks // {schedulerData.events}
            }
    
  3. Need to specify 'name' property on the field you're adding, which should match data field. Otherwise loading/saving will not work automatically.

        const field = WidgetHelper.createWidget({
            type:'combo', 
            name : 'MyField', // matches data field in Task model
            ref : 'myField' // then you can always get it in the editor as editor.widgetMap.myField
            label: 'My field',
            id: "MyField", // not sure this is needed
            items: mapOfIdAndText
        });
    

To summarize, auto loading/saving to custom fields in Event Editor works if:

  1. Data field is defined on the model
  2. Widget field has 'name' property which matches the data field

Best,
Pavel

P.S. Please note, all code snippets are simple javascript and they are not supposed to be used directly. Please adjust them according to your needs and to React application you're working with.

Pavlo Miklashevych
Sr. Frontend Developer


Post by Rama »

I can get the value of custom field using approach you mentioned. In the provided react samples - the way to set data is following and incrementing resourceVersion and eventVersion when we receive data from api.

But how do set new data in the store. Is there an comprehensive example in react where "eventStore" is used

{
        barMargin: 5,
        selectedEvent: '',
        eventsVersion: 0,
        resourcesVersion: 0,
        events: null,
        resources: null,
        timeRanges: null
    }

Post by pmiklashevich »

Hello,

I see you're confused by our Basic example in example/react/typescript. This example loads data to individual stores. And our Scheduler Wrapper (Scheduler/examples/react/_shared/src/lib/BryntumScheduler.js) which is used in this demo contains "shouldComponentUpdate" function which let react know if scheduler should be rerendered. In the demo we set version to 0 initially:

    state = {
        barMargin        : 5,
        selectedEvent    : '',
        eventsVersion    : 0,
        resourcesVersion : 0,
        events           : null,
        resources        : null,
        timeRanges       : null
    };

and after load we increment it to let shouldComponentUpdate know that data changed:

        fetch('data/data.json').then(response => {
            response.json().then(data => {
                this.setState({
                    // Increment these to notify BryntumScheduler that it should process events/resources
                    eventsVersion    : 1, // increment current version
                    resourcesVersion : 1, // increment current version

                events     : data.events.rows,
                resources  : data.resources.rows,
                timeRanges : data.timeRanges.rows
            });
        });
    });

But the approach above is a bit out-of-dated. We kept it to show backward compatibility. Basically we recommend to use our CrudManager to handle all data at once. You don't need to provide events or eventStore individually. You can see how it works in Filtering and RecurringEvents demos:
Scheduler/examples/react/typescript/filtering
Scheduler/examples/react/typescript/recurring-events

So if we take for example Scheduler/examples/react/typescript/filtering/src/App.tsx, you can see that only crudManager is assigned:

            <BryntumScheduler
                ref = {scheduler}
                barMargin         = {schedulerConfig.barMargin}
                columns           = {schedulerConfig.columns}
                crudManager       = {schedulerConfig.crudManager} // <= HERE
                eventBodyTemplate = {schedulerConfig.eventBodyTemplate}
                eventColor        = {schedulerConfig.eventColor}
                eventStyle        = {schedulerConfig.eventStyle}
                eventRenderer     = {schedulerConfig.eventRenderer}
                rowHeight         = {schedulerConfig.rowHeight}
                startDate         = {schedulerConfig.startDate}
                endDate           = {schedulerConfig.endDate}
                eventEditFeature  = {schedulerConfig.eventEditFeature}
                filterBarFeature  = {schedulerConfig.filterBarFeature}
                stripeFeature     = {schedulerConfig.stripeFeature}
                timeRangesFeature = {schedulerConfig.timeRangesFeature}

        />

and in Scheduler/examples/react/typescript/filtering/src/schedulerConfig.ts crudManager is defined as:

    crudManager : {
        autoLoad : true,
        transport : {
            load : {
                url : 'data/data.json'
            }
        }
    },

In case you need to provide a specific class for your event model, please use eventStore config and modelClass config. For example:

    crudManager : {
        eventStore : {
            modelClass : Task // where Task is your class from "class Task extends EventModel {..."
        },
        autoLoad : true,
        transport : {
            load : {
                url : 'data/data.json'
            }
        }
    },

You can read mode about CrudManager and data format expected by the CrudManager in this guide: https://www.bryntum.com/docs/scheduler/#guides/data/crud_manager.md

Best,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post by Rama »

Is there a way we can intercept events and handle api calls on our own rather than using "transport" option ?


Post by mats »

Sure, you can always get all the data from our stores and send them to your server manually. But we recommend you to use our suggested way.


Post by Rama »

Hi Mats,

Based on your suggestion - we are moving our implementation to CrudManager - Things are starting to work but I am seeing one strange behavior in the GET request being sent by CrudManager -

Following is what crud manager sends in Query parameters -

{"type":"load","requestId":15912787758380,"stores":["scheduleTypes","events","resources","store-6"]}

My crud manager configuration looks like following
And I have seen this - store-6 comes only when I include assignmentStore in the crud manager. Else it goes away but then assignments don't get saved.
Is this a convention to identify assignment store by "store-6"

const ScheduleTypeStore = new AjaxStore({
    storeId: "scheduleTypes",
});

const SchedulerCrudManager = {
    autoLoad : true,
    autoSync: true,
    eventStore: {
        modelClass: CustomEvent
    },
    stores: [ScheduleTypeStore],
    resourceStore: ResourceStore,
    assignmentStore: AssignmentStore,
    transport: {
        load: {
            url: "CrudManagerData.json"
        },
        sync: {
            url: "CrudManagerData"
        }
    }
};

Post by pmiklashevich »

Hello,

EventStore and ResourceStore have storeId specified by default ("events" and "resources"). But Assignments store doesn't have default storeId. It will be fixed in the next major release. Meanwhile please specify "assignments" as an ID for the AssignmentStore manually, similar to what we do in our Scheduler/examples/multiassign-with-dependencies/app.js example:

const assignmentStore = new AssignmentStore({
    id : 'assignments'
});
....
    crudManager : {

    assignmentStore,

    transport : {
        load : {
            url : 'data/data.json'
        }
    },

    autoLoad : true
}

Thanks,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post Reply