Our pure JavaScript Scheduler component


Post by tboegner »

Hi Dev-Team,

in my scenario with two schedulers (react components) I have bound the event store via REST using the following configuration:

eventStore = {{
  autoLoad: true,
  autoCommit: true,
  allowNoId: false,
  useRestfulMethods: true,
  writeAllFields: true,
  readUrl:   eventStoreUrl,
  createUrl: eventStoreUrl,
  deleteUrl: eventStoreUrl,
  updateUrl: eventStoreUrl,
  listeners: {
    refresh: this.onEventStoreRefresh,
    add: this.onEventStoreAdd,
    remove: this.onEventStoreRemove,
    update: this.onEventStoreUpdate
  }
}}

Let's assume the eventStoreUrl for scheduler 1 is

localhost:8086/rest/events/machines?token=A

and for scheduler 2 is

localhost:8086/rest/events/jobs?token=A

(the value for token may vary).

When an event is moved from scheduler 2 into scheduler 1, in most cases a POST request on URL of scheduler 1 is called with the new ResourceId in the JSON body. Additionally the event is deleted by a DELETE request on scheduler 2. This is the expected behavior.

However, sometimes instead of the POST request one or multiple PUT requests are called with missing or previous/obsolete ResourceId are called. This leads to loss of the assignment of the concerned event.

Could you please check if this is a bug?

Best Regards
Timo


Post by mats »

Could you please provide a data set to use to debug this? Do you have same ids of the resources in your two schedulers?


Post by tboegner »

The ids of the resources are different (see attached files for data).

You can see a demo of the application via this link. My impression is, that chances for seeing the problem are high, if you place an event at the earliest possibility in one of the resources of the upper scheduler first of all.

Attachments
jobHolders.json
Resources for Scheduler 2 (Jobs)
(439 Bytes) Downloaded 79 times
jobs.json
Available events/jobs
(8.28 KiB) Downloaded 82 times
machines.json
Resources for Scheduler 1 (Machines)
(300 Bytes) Downloaded 74 times

Post by pmiklashevich »

Hello,

I've checked your dataset. It looks good. It's pretty simple. Also I checked out your online demo. When I drag an event from Jobs to Machines I always see 3 requests in this order:

  • PUT to jobs with the updated resourceId equal to null. It looks like you unassign the event first.
  • POST to the machines. So you add to the machines store
  • Delete to jobs. So you remove from the jobs.

sometimes instead of the POST request one or multiple PUT requests are called with missing or previous/obsolete ResourceId are called

I was not able to see that.

I'm not sure about the drag and drop implementation you're using. It's always better to provide a full runnable testcase with minimum code and mock up your server responses so it's only the client-side code (as it's requested in this guide). But before you submit a testcase please try to copy the record using this API: https://www.bryntum.com/docs/scheduler/#Scheduler/model/EventModel#function-copy

const record = new Model({ name : 'Super model', hairColor : 'Brown' });
const clone = record.copy({ name : 'Super model clone' })

Note, the copy method returns a new instance, so no need to use "new". We will fix the docs.

So simple algorithm looks like:

  • on drop you clone the record and apply a new resource retrieved from the target position
  • add the clone to the first scheduler store
  • remove the original record from the second scheduler store

Best wishes,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post by tboegner »

Hi Pavel,

thank you for your reply. I don't do the copying by myself, instead I use the functionality of scheduler / event store, which is configured as follows:

        return (<div><BryntumScheduler
            ref = {'scheduler'}
            autoHeight = {true}
            startDate  = {this.startDate}
            endDate    = {this.endDate}
            columns = {[
                { text : this.props.resourceTitle, field : 'name', flex : 1 }
            ]}
            viewPreset = {{
                base : 'hourAndDay',
                headers : [
                  {
                      unit       : 'day',
                      dateFormat : 'dddd'
                  },
                  {
                       unit : 'hour',
                       dateFormat : 'HH'
                  }
                ]
            }}

        forceFit = {true}
        //allowOverlap = {false}
        timeResolution = {{
            unit: 'hour',
            increment: 1
        }}
        workingTime = {{
            fromDay : 0,
            toDay: 6,
            fromHour : this.props.workStartHour,
            toHour : this.props.workEndHour
        }}

        eventEditFeature = {{
            disabled: true
        }}
        eventResizeFeature = {{
            disabled : true
        }}
        eventDragCreateFeature = {{
            disabled: true
        }}

        eventDragFeature = {{
            constrainDragToTimeline: false,
            validatorFn: this.validateDrag,
            validatorFnThisObj : this
        }}

        createEventOnDblClick = {false}
        scheduleMenuFeature = {{
            items: {
                addEvent: false
            }
        }}
        enableDeleteKey = {false}
        eventMenuFeature = {{
            items: {
                deleteEvent: false
            }
        }}

        readOnly = {this.props.showSolution}
        resourceStore = {{
            autoLoad: true,
            allowNoId: false,
            useRestfulMethods: true,
            readUrl: resourceStoreUrl,
            listeners: {
                load: this.onResourceStoreLoad
            }
        }}
        eventStore = {{
            autoLoad: true,
            autoCommit: true,
            allowNoId: false,
            useRestfulMethods: true,
            writeAllFields: true,
            readUrl:   eventStoreUrl,
            createUrl: eventStoreUrl,
            deleteUrl: eventStoreUrl,
            updateUrl: eventStoreUrl,
            listeners: {
                refresh: this.onEventStoreRefresh,
                add: this.onEventStoreAdd,
                remove: this.onEventStoreRemove,
                update: this.onEventStoreUpdate
            }
        }}

    />
    </div>);

The requests come from the configured scheduler/event store. In most cases this POST/DELETE sequence occurs correctly. But there are cases, where the POST call is missing, so the event is just deleted.

Best Regards,
Timo


Post by pmiklashevich »

Hello Timo,

I checked how the copying between schedulers works in our demos and spot the following problem (when IDs match in both schedulers): https://github.com/bryntum/support/issues/2037

About the requests you see, I was not able to reproduce it. Please try to apply this code with the configuration similar to what you sent to the Scheduler/examples/drag-between-schedulers/app.js demo. (Not React, because this is related to core functionality). Then drag a task from one scheduler to another. See 2 requests are sent: DELETE and POST.

const scheduler1 = new Scheduler({
    ref               : 'top-scheduler',
    appendTo          : 'container',
    flex              : '1 1 50%',
    multiEventSelect  : true,
    resourceImagePath : '../_shared/images/users/',

features : {
    eventDrag : {
        // Allow drag outside of this Scheduler
        constrainDragToTimeline : false
    }
},

columns : [
    {
        type  : 'resourceInfo',
        text  : 'New York office',
        width : '15em'
    }
],

resources : [
    { id : 1, name : 'Arcady', role : 'Core developer', eventColor : 'purple' },
    { id : 2, name : 'Dave', role : 'Tech Sales', eventColor : 'indigo' },
    { id : 3, name : 'Henrik', role : 'Sales', eventColor : 'blue' },
    { id : 4, name : 'Linda', role : 'Core developer', eventColor : 'cyan' },
    { id : 5, name : 'Maxim', role : 'Developer & UX', eventColor : 'green' },
    { id : 6, name : 'Mike', role : 'CEO', eventColor : 'lime' },
    { id : 7, name : 'Lee', role : 'CTO', eventColor : 'orange' }
],

eventStore : {
    data : [
        {
            id           : 1,
            resourceId   : 1,
            name         : 'First Task',
            startDate    : new Date(2018, 0, 1, 10),
            duration     : 2,
            durationUnit : 'h'
        }
    ],
    autoLoad          : true,
    autoCommit        : true,
    allowNoId         : false,
    useRestfulMethods : true,
    writeAllFields    : true,
    readUrl           : 'data/read1.json',
    createUrl         : 'data/create1.json',
    deleteUrl         : 'data/delete1.json',
    updateUrl         : 'data/update1.json'
},

startDate  : new Date(2018, 0, 1, 6),
endDate    : new Date(2018, 0, 2, 20),
viewPreset : 'hourAndDay'
});

new Splitter({
    appendTo : 'container'
});

// eslint-disable-next-line no-unused-vars
const scheduler2 = new Scheduler({
    ref               : 'bottom-scheduler',
    appendTo          : 'container',
    flex              : '1 1 50%',
    partner           : scheduler1,
    resourceImagePath : '../_shared/images/users/',
    // hideHeaders : true,
    features          : {
        eventDrag : {
            // Allow drag outside of this Scheduler
            constrainDragToTimeline : false
        }
    },

columns : [
    {
        type  : 'resourceInfo',
        text  : 'Stockholm office',
        width : '15em'
    }
],

resources : [
    { id : 11, name : 'Angelo' },
    { id : 12, name : 'Gloria' },
    { id : 13, name : 'Madison' },
    { id : 14, name : 'Malik' },
    { id : 15, name : 'Mark' },
    { id : 16, name : 'Rob' }
],
eventStore : {
    data : [
        {
            id           : 11,
            resourceId   : 11,
            name         : 'Implement Feature X',
            startDate    : new Date(2018, 0, 1, 10),
            duration     : 2,
            durationUnit : 'h'
        }
    ],
    autoLoad          : true,
    autoCommit        : true,
    allowNoId         : false,
    useRestfulMethods : true,
    writeAllFields    : true,
    readUrl           : 'data/read2.json',
    createUrl         : 'data/create2.json',
    deleteUrl         : 'data/delete2.json',
    updateUrl         : 'data/update2.json'
}
});
Снимок экрана 2020-12-03 в 19.30.46.png
Снимок экрана 2020-12-03 в 19.30.46.png (771.34 KiB) Viewed 630 times

Could you please submit a runnable testcase, so we can inspect it? You can apply minimal changes to one of our demo, zip it up and attach here. We will investigate and maybe update the one I mentioned above, or create a new one if it's not related. Thanks!

Best regards,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post Reply