Our pure JavaScript Scheduler component


Post by dev team »

Issue
We are using the Scheduler implementation of the CrudManager where we are experiencing issues when checking the
CrudManager for changes.

this.scheduler.crudManager.changes;

A large % of attempts will resolve in no changes which will not proceed with our desired implementation. This affects:

  • EventModel Create
  • EventModel Edit
  • EventModel Drag
  • EventModel Resize

Even when attempting to use the following inbuilt event, occasionally the event is still not being hit.

crudManager.on('hasChanges', function (crud) {
        // ... updateJob logic called here
    });

Current Implementation

private async createCrudManager(): Promise<Partial<CrudManagerConfig>> {
    const resourceStore = await this.generateResourceStore(this.schedulerEngineers, this.schedulerTeams);
    const eventStore = await this.generateEventStore(this.schedulerJobs);
    const assignmentStore = await this.generateAssignmentStore(false, eventStore);

return {
  resourceStore,
  eventStore,
  assignmentStore,
};
  }
this.scheduler = new Scheduler({
      appendTo: this.host,
      startDate,
      endDate,
      columns,
      height,
      crudManager,
      ...
 })

Below is our implementation when we want to create or update an EventModel.

@Method()
  public async updateJob(record: JobModel, job: Partial<Job>): Promise<void> {
    const { assignmentStore } = this.scheduler;

// Prevent multiple commits from this flow.
assignmentStore.suspendAutoCommit();

// suspend scheduler refreshing
this.scheduler.suspendRefresh();

// If it was a provisional event, passed in here from drag-create or dblclick or contextmenu,
// it's now it's no longer a provisional event and will not be removed in the focusout handler
// Also, when promoted to be permanent, auto syncing will kick in if configured.
record.isCreating = false;

record.beginBatch();
for (const jobKey in job) {
  record.set(jobKey, job[jobKey]);
}
record.endBatch();

const changes: CrudManagerChanges = this.crudManagerChanges;
if (changes) {
	// ... Using the identified changes from the CrudManager, create patch documents to shape the payload the server requires.
	// ... Emit Event to update Job
}
else {
  // Revert changes, Reset the state management and redraw once
  record.revertChanges();
  assignmentStore.resumeAutoCommit();
  await this.scheduler.resumeRefresh(true);
}

Post by alex.l »

Are we talking about SchedulerPro or Scheduler ? I see you licensed to use Pro version, so will answer for SchedulerPro version.
Since CrudManager for Pro version is a part of ProjectModel, I tried in online example https://bryntum.com/examples/scheduler-pro/bigdataset/
in console

schedulerPro.project.on('hasChanges', ()=>{console.log(1)})

And tried to resize event. I see it logged.

I cannot test the code snippet you provided. I need more context. Try to apply required changes to one of our demos and attach a runnable test case here if possible. I am also not sure about manipulation with isCreating flag.
Also this

record.beginBatch();
for (const jobKey in job) {
  record.set(jobKey, job[jobKey]);
}
record.endBatch();

May be done as

record.set(job);

It would be great to see a complete example, as I understood it's not some general event resize, but resize with extra logic applied?

All the best,
Alex


Post by dev team »

Hi Alex,

We are using the Scheduler from within the SchedulerPro package as recurrence is not fully supported within SchedulerPro, but that is another story.

Trying to replicate the issue on demo projects doesn't seem to be successful unfortunately.


ResizeEvent
Here is a snipped as to how we are handing the ResizeEvent.

beforeEventResizeFinalize: async ({context}) => {
          if (this.hasClaims(['Job.CanUpdate'])) {
            // Update the start and end dates:
            const jobPatch = {
              startDate: new Date(context.startDate),
              endDate: new Date(context.endDate),
            };

        // Apply the update:
        await this.updateJob(context.eventRecord, jobPatch);

        // Prompt CRUDManager to be aware of the changes made:
        await this.scheduler.crudManager.acceptChanges();
        await this.scheduler.resumeRefresh(true);
      }

      // Prevent the Scheduler from processing this event any further:
      context.finalize(false);
    },

As you can see, we ensure every CRUD operation flows through the updateJob method for consistency. We require a jobPatch: Partial<Job>. This will perform the diff calculation, set the EventModel values and then we assume the CrudManager should be aware of the changes to the EventModel at this time ready for further processing server side via a js interop call.


EventDrop
We have the similar logic and flow for the EventDrop as we do for ResizeEvent. Please see below.

beforeEventDropFinalize: async ({ context }) => {
          if (this.hasClaims(['Job.CanUpdate'])) {
            // Update the start and end dates:
            const jobPatch = {
              startDate: new Date(context.startDate),
              endDate: new Date(context.endDate),
            };

        // Get the data for the new resource (team/engineer):
        const newResourceData = context.newResource.data;

        if (newResourceData) {
          const resourceType = newResourceData.resourceType;

          // If dragged to an engineer set the engineer id on the event record and null out the team id:
          if (resourceType === ResourceType.Engineer) {
            context.eventRecord.engineerId = newResourceData.id;
            context.eventRecord.teamId = null;
          }
          // If dragged to a team set the team id on the event record and null out the engineer id:
          else if (resourceType === ResourceType.Team) {
            context.eventRecord.teamId = newResourceData.id;
            context.eventRecord.engineerId = null;
          }
        }

        // Apply the update:
        await this.updateJob(context.eventRecord, jobPatch);

        // Prompt CRUDManager to be aware of the changes made:
        await this.scheduler.crudManager.acceptChanges();
        await this.scheduler.resumeRefresh(true);
      }

      // Prevent the Scheduler from processing this event any further:
      context.finalize(false);
    },

hasChanges Event
Regarding the hasChanges event, we have attempted to use this but we end up in the same situation where using the same test data set and data edits, we will sometimes be provided with crudManager.changes and other times the event will not be hit.


Additional
For our understanding, please can you explain how the engine of the crudManger works, from its known Stores:

  • How does it track changes to records within those stores.
  • Whether these actions are asynchronous.
  • Is there a manual trigger which will force the crudManager to check for changes.

I feel this would be valuable information for us moving forward.

Thanks


Post by alex.l »

https://bryntum.com/docs/scheduler-pro/api/Scheduler/feature/EventDrag#event-beforeEventDropFinalize is not async, please check example in docs how to use async checks to not have it reverted back.

 scheduler.on('beforeeventdropfinalize', ({ context }) => {
     context.async = true;
     setTimeout(() => {
         // async code don't forget to call finalize
         context.finalize();
     }, 1000);
 })

Let us know if it helped,

All the best,
Alex


Post by dev team »

Hi Alex,

We've tried your suggestion without success.

I've attached a screen recording of the crudManager changes returning as null after setting a record.

We've done some debugging in the getter for crudManager changes on the Bryntum side of things and it seems like me.modified.value has no rawModifications on it which seems to be why it is coming back as null. Hopefully this is helpful in getting to the bottom of this.

This is our get method for CrudManager changes as seen in the screen recording:

  /**
   * Return the Scheduler's CrudManager Changes object
   */
  get crudManagerChanges(): CrudManagerChanges {
    return this.scheduler.crudManager.changes as CrudManagerChanges;
  }

It is just retrieving the our scheduler's crudManager.changes

Attachments
Screen Recording 2022-11-09 at 17.02.40.mov
(21.95 MiB) Downloaded 36 times

Post by alex.l »

changes are not available synchronously, it (may be) need to be calculated, so no guarantee it will be there. There are many scenarios why changes won't be appeared immediately.
To help you here, we need a test case that we can debug. Video is not enough unfortunately.
hasChanges event may be triggered by any store attached to crudManager. As example, by assignmentStore, but eventStore won't have changes at the moment.
Try to listen to https://bryntum.com/docs/scheduler/api/Scheduler/model/ProjectModel#event-change or https://bryntum.com/docs/scheduler/api/Scheduler/model/ProjectModel#event-dataReady to have changes ready.

But for complete solution we need a runnable test case.

All the best,
Alex


Post by dev team »

Hello,

Thank you for the response.

So the events mentioned are triggered with the changes correctly present but only when an event is dragged on the scheduler itself and not when we use

eventRecord.set()

which in theory should trigger the changes.

This even happens with the

scheduler.crudManager.on('hasChanges')

event.

It seems that there is some logic happening in the background which trigger the change detection -

eventRecord.set()

doesn't seem meet the requirements.

eventRecord.setDuration()

correctly triggers the changes so it seems that only certain properties on the event record are being watched for changes? Or is this a bug? Any ideas?

Many thanks.


Post by alex.l »

What changes are not trigger the change event? Could you please provide us code examples to reproduce it?

I tried here https://bryntum.com/products/scheduler/examples/basic/
In console:

scheduler.eventStore.on('change', () => console.log('changed'))
ev=scheduler.eventStore.first
ev.set({ name : 'Mark' })

I see the event triggered.

Do you have fields that you changed with eventRecord.set() specified in EventModel? Are those custom added fields or default fields? What should I do to reproduce that?

All the best,
Alex


Post Reply