Our pure JavaScript Scheduler component


Post by Jerome Cauchon »

Hi
i'm using the crud manager with the scheduler pro. My event model contains a custom property, linkedEvents, which is an int array.

My workflow is:

  1. Using the event editor, i modified my linkedEvents property. Once my event is saved, the crudManager call my backend with the following payload:
{
   "type":"sync",
   "requestId":16305158802843,
   "events":{
      "updated":[
         {
            "id":5,
            "startDate":"2021-07-07T10:00:00-04:00",
            "endDate":"2021-09-07T12:00:00-04:00",
            "duration":62.083333333333336,
            "durationUnit":"day",
            "cls":"stretch ",
            "iconCls":"b-fa b-fa-lock",
            "name":"#180971 Red Oak",
            "exceptionDates":[
               
], "resourceId":5, "allDay":false, "eventColor":"blue", "desc":"the description", "eventType":"Drying", "linkedEvents":[ 38 ], "lumberType":"", "recipe":null, "initialWoodMoisture":0, "targetWoodMoisture":0, "woodQuantity":0, "detailedComment":"", "tooltipNote":"", "isLocked":false } ] } }
  1. My .Net Core backend apply the update on the targeted event ID #5 but also update the linked event ID #38 . Here's the response of my sync call:
{
   "success":true,
   "requestId":16305158802843,
   "message":null,
   "code":0,
   "events":{
      "rows":[
         {
            "id":38,
            "resourceId":37,
            "name":"#180971 Red Oak",
            "desc":"the description",
            "startDate":"2021-09-08T11:00:00",
            "endDate":"2021-09-15T14:00:00",
            "eventType":"Order",
            "eventColor":"blue",
            "iconCls":"b-fa b-fa-lock",
            "cls":"stretch",
            "linkedEvents":[
               5
            ],
            "lumberType":"AmericanWalnut",
            "recipe":null,
            "initialWoodMoisture":0,
            "targetWoodMoisture":0.0,
            "woodQuantity":0,
            "detailedComment":"dasgfsfdghsdhgf",
            "tooltipNote":"",
            "isLocked":false
         }
      ],
      "removed":[
         
], "updated":[ ] }, "resources":{ "rows":[
], "removed":[ ], "updated":[ ] } }

The related event, ID #38, should be updated with new values in linkedEvents but unfortunately it doesn't.

I made a test by updating the name field in the related event and it succeeded! So i'm suspecting that my array field update does not trigger en update in the event store.

Any idea about my issue?

Thanks


Post by mats »

Can you please share your model and scheduler configs?


Post by Jerome Cauchon »

The model:

type Event = {
  id: number;
  resourceId: number;
  name: string;
  startDate: string;
  endDate: string;
  eventType: string;
  eventColor: string;
  iconCls: string;
  isLocked: boolean;
  cls: string;
  linkedEvents: number[];
  lumberType: string;
  recipe: Recipe;
  woodQuantity: number;
  initialWoodMoisture: number;
  targetWoodMoisture: number;
  tooltipNote: string;
  detailedComment: string;
};

The config

const schedulerConfig = {
    eventRenderer: ({ eventRecord, renderData }: { eventRecord: PlanifSchedulerEventModel; renderData: { iconCls: string | null } }) => {
      // Should be moved in an external class to manage event style based on recipe, lock status and others
      renderData.iconCls = eventRecord.data?.isLocked ? 'b-fa b-fa-lock' : null;
      return eventRecord.name;
    },
    resourceImagePath: 'users/',
    rowHeight: 30,
    viewPreset: {
      base: 'weekAndDayLetter',
      headers: [
        {
          unit: 'week',
          dateFormat: 'DD/MM/YYYY',
        },
        {
          unit: 'day',
          dateFormat: 'd1',
        },
      ],
    },
    eventStyle: 'rounded',
    createEventOnDblClick: false,
    enableDeleteKey: false,
    timeRangesFeature: {
      showCurrentTimeLine: true,
      currentDateFormat: 'DD-MM-YYYY HH:mm',
      narrowThreshold: 10,
    },
    features: {
      group: 'type',
      dependencies: false,
      eventMenu: false,
      cellMenu: false,
      timeAxisHeaderMenu: false,
      eventTooltip: getEventTooltip(),
    },
    listeners: {
      beforeEventEdit: ({ eventRecord }: { eventRecord: SchedulerEventModel }) => {
        showEditor(eventRecord);
        return false;
      },
      beforeEventDelete: ({ eventRecords }: { eventRecords: PlanifSchedulerEventModel[] }) => {
        if (eventRecords.length === 1 && !eventRecords[0].data?.isLocked) {
          handleDelete(eventRecords[0]);
        }
        return false;
      },
      eventMenuBeforeShow: ({ eventRecord, items }: { eventRecord: PlanifSchedulerEventModel; items: { deleteEvent: { disabled: boolean | undefined } } }) => {
        items.deleteEvent.disabled = eventRecord.data?.isLocked;
      },
      eventSelectionChange: ({ selected }: { selected: PlanifSchedulerEventModel[] }) => updateSelectedTimeRange(selected[0]),
      afterEventDrop: ({ draggedRecords }: { draggedRecords: PlanifSchedulerEventModel[] }) => updateSelectedTimeRange(draggedRecords[0]),
      eventResizeEnd: ({ eventRecord }: { eventRecord: PlanifSchedulerEventModel }) => updateSelectedTimeRange(eventRecord),
    },
    columns: [
      { text: '', field: 'name', width: 130 },
      {
        field: 'type',
        hidden: true,
        groupRenderer: ({ groupRowFor }: { groupRowFor: string }) => t(`planning:${groupRowFor}`, { count: 2 }),
      },
    ],
    crudManager: {
      autoLoad: false,
      autoSync: true,
      writeAllFields: true,
      transport: {
        load: {
          url: `${baseUrl}/load`,
        },
        sync: {
          url: `${baseUrl}/sync`,
        },
      },
      listeners: {
        // Will be called after data is fetched but before it is loaded into stores
        beforeLoadApply({ response }: { response: { events: { rows: Event[] } } }) {
          response.events.rows = response.events.rows.map((event: Event) => ({
            ...event,
            resizable: !event.isLocked,
            draggable: !event.isLocked,
          }));
        },
        syncFail: ({ response, source }: { response: { message: string }; source: AbstractCrudManager }) => {
          errorActions.setErrorMessage(response.message);
          source.load(getLoadRequest());
        },
      },
      validateResponse: !process.env.NODE_ENV || process.env.NODE_ENV === 'development',
    },
  };

Post by Jerome Cauchon »

It seems that my field is spread in multiples fields

detailedComment: (...)
draggable: (...)
eventColor: (...)
eventStyle: (...)
eventType: (...)
exceptionDates: (...)
generation: 1
iconCls: (...)
id: (...)
initialWoodMoisture: (...)
isLocked: (...)
linkedEvents: Array(0)
linkedEvents.0: 5
lumberType: (...)
meta: {modified: {…}, map: {…}, committing: false}
milestoneWidth: (...)

Post by alex.l »

I don't see you applied your extended event model class to eventStore

    project : {
        autoLoad  : true,
        transport : {
            load : {
                url : 'data/data.json'
            }
        },
        eventModelClass : ExtendedEvent, // something like this, as example

https://bryntum.com/docs/scheduler/#Scheduler/model/mixin/ProjectModelMixin#config-eventModelClass

Is it possible to share a runnable app?

This example shows how to use complex extra fields, may be helpful to check https://bryntum.com/examples/scheduler-pro/nested-events/

All the best,
Alex


Post by Jerome Cauchon »

Thanks for your fast reply.

Should i use project config or crudManager config on my scheduler?

I tried with the following: I got a log for the main event(id: 5), but not for the related event(id: 38).

It's pretty complicated to share a runnable example with you since my scheduler is part of big application.

class Task extends SchedulerEventModel {
  static get fields() {
    return [
      {
        name: 'linkedEvents',
        isEqual: (oldValue: any, value: any) => {
          console.log('???', oldValue, value);
          return ObjectHelper.isEqual(oldValue, value);
        },
        type: 'array',
      },
    ];
  }
}
...

crudManager: {
      autoLoad: false,
      autoSync: true,
      writeAllFields: true,
      eventStore: {
        modelClass: Task, // event model config
      },
      transport: {
        load: {
          url: `${baseUrl}/load`,
        },
        sync: {
          url: `${baseUrl}/sync`,
        },
      },
      listeners: {
        // Will be called after data is fetched but before it is loaded into stores
        beforeLoadApply({ response }: { response: { events: { rows: Event[] } } }) {
          response.events.rows = response.events.rows.map((event: Event) => ({
            ...event,
            resizable: !event.isLocked,
            draggable: !event.isLocked,
          }));
        },
        syncFail: ({ response, source }: { response: { message: string }; source: AbstractCrudManager }) => {
          errorActions.setErrorMessage(response.message);
          source.load(getLoadRequest());
        },
      },
      validateResponse: !process.env.NODE_ENV || process.env.NODE_ENV === 'development',
    },

Post by arcady »

Should i use project config or crudManager config on my scheduler?

If you use Scheduler Pro component then it should be project. Scheduler Pro ProjectModel encapsulates Crud Manager so it's able to load and persist its data. So a standalone Crud Manager is not used there.


Post by Jerome Cauchon »

I'll try it and give you feedback soon. Thanks arcady.


Post by Jerome Cauchon »

With our exchanges, i realized that i was using the wrong component. BryntumScheduler instead of BryntumSchedulerPro. I'll fix this and come back when it will be fixed.


Post by Jerome Cauchon »

Ok after some issues during my migration from Scheduler to Scheduler Pro, i'm back. Unfortunately, i have the same problem. The updated array property of my object is not synced. My model is defined as follow. The updated record does not hit when my my updated record is returned from the sync!

export class SchedulerProEventModel extends EventModel {
  static get fields(): unknown[] {
    return [
      'detailedComment',
      'initialWoodMoisture',
      'isLocked',
      'lumberType',
      'recipe',
      'targetWoodMoisture',
      'tooltipNote',
      'woodQuantity',
      {
        name: 'linkedEvents',
        isEqual: (oldValue: any, value: any) => {
          console.log('???', oldValue, value);
          return ObjectHelper.isEqual(oldValue, value);
        },
        type: 'array',
      },
    ];
  }
}

Post Reply