Hi,
I want to retrieve scheduled tasks and unplanned tasks in one call to our server. Right now my app is calling scheduler.crudManager.load() and unplannedGrid.store.load() to do this. I want to include the unplanned data in the JSON retrieved by scheduler.crudManager.load(), then use the unplanned data in its JSON to load into the unplannedGrid. How would I do this?
Support Forum
Hey juliemilligan,
Thanks for reaching out.
You could use the following event listener https://bryntum.com/products/scheduler/docs/api/Scheduler/data/CrudManager#event-beforeResponseApply and apply the changes to a proper format to the scheduler, and also apply the data to the unplannedGrid.
Best regards,
Márcio
You can load it with a crudManager by adding a crudStore
First of all, this is how my data looks like
{
"success" : true,
"resources" : {
"rows" : [
{
"id" : 1,
"name" : "Arcady",
"role" : "Core developer"
},
{
"id" : 2,
"name" : "Dave",
"eventColor" : "green",
"role" : "Tech Sales"
},
{
"id" : 3,
"name" : "Henrik",
"eventColor" : "green",
"role" : "Sales"
},
{
"id" : 4,
"name" : "Linda",
"eventColor" : "red",
"role" : "Core developer"
},
{
"id" : 5,
"name" : "Maxim",
"eventColor" : "red",
"role" : "Developer & UX"
},
{
"id" : 6,
"name" : "Mike",
"eventColor" : "red",
"role" : "CEO"
},
{
"id" : 7,
"name" : "Lee",
"role" : "CTO"
},
{
"id" : 8,
"name" : "Amit",
"role" : "Core developer"
},
{
"id" : 9,
"name" : "Kate",
"eventColor" : "blue",
"role" : "Tech Sales"
},
{
"id" : 10,
"name" : "Jong",
"eventColor" : "blue",
"role" : "Sales"
},
{
"id" : 11,
"name" : "Lola",
"role" : "Core developer"
},
{
"id" : 12,
"name" : "Lisa",
"role" : "UX"
},
{
"id" : 13,
"name" : "Steve",
"role" : "COO"
},
{
"id" : 14,
"name" : "Malik",
"role" : "CFO"
}
]
},
"events" : {
"rows" : [
{
"id" : "r1",
"resourceId" : 1,
"name" : "Restart server (not draggable)",
"iconCls" : "b-fa b-fa-server",
"startDate" : "2025-12-01T08:00:00",
"duration" : 3,
"durationUnit" : "h",
"draggable" : false,
"resizable" : true
},
{
"id" : "r2",
"resourceId" : 1,
"name" : "Upgrade memory",
"iconCls" : "b-fa b-fa-laptop",
"startDate" : "2025-12-01T15:00:00",
"cls" : "",
"duration" : 3,
"durationUnit" : "h",
"draggable" : true,
"resizable" : true
},
{
"id" : "r3",
"resourceId" : 2,
"name" : "Visit customer",
"iconCls" : "b-fa b-fa-user",
"startDate" : "2025-12-01T09:00:00",
"cls" : "",
"duration" : 3,
"durationUnit" : "h",
"draggable" : true,
"resizable" : true
},
{
"id" : "r4",
"resourceId" : 3,
"name" : "Arrange meetup",
"iconCls" : "b-fa b-fa-users",
"startDate" : "2025-12-01T09:00:00",
"cls" : "",
"duration" : 3,
"durationUnit" : "h",
"draggable" : true,
"resizable" : true
},
{
"id" : "r5",
"resourceId" : 7,
"name" : "Make coffee",
"startDate" : "2025-12-01T12:00:00",
"iconCls" : "b-fa b-fa-coffee",
"duration" : 4,
"durationUnit" : "h",
"draggable" : true,
"resizable" : true
},
{
"id" : "r6",
"resourceId" : 9,
"name" : "Conference prep",
"iconCls" : "b-fa b-fa-building",
"startDate" : "2025-12-01T09:00:00",
"cls" : "Special",
"duration" : 3,
"durationUnit" : "h",
"draggable" : true,
"resizable" : true
},
{
"id" : "r16",
"resourceId" : 11,
"name" : "Presentation",
"iconCls" : "b-fa b-fa-video",
"startDate" : "2025-12-01T13:00:00",
"cls" : "Special",
"duration" : 2,
"durationUnit" : "h",
"draggable" : true,
"resizable" : true
}
]
},
"timeRanges" : {
"rows" : []
},
"unplannedTasks" : {
"rows" : [
{
"id" : 1,
"name" : "Fun task",
"duration" : 4,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-beer"
},
{
"id" : 2,
"name" : "Medium fun task",
"duration" : 8,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-cog"
},
{
"id" : 3,
"name" : "Outright boring task",
"duration" : 2,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-book"
},
{
"id" : 4,
"name" : "Inspiring task",
"duration" : 2,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-book"
},
{
"id" : 5,
"name" : "Mysterious task",
"duration" : 2,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-question"
},
{
"id" : 6,
"name" : "Answer forum question",
"duration" : 4,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-life-ring"
},
{
"id" : 7,
"name" : "Gym",
"duration" : 1,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-dumbbell"
},
{
"id" : 9,
"name" : "Book flight",
"duration" : 7,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-plane"
},
{
"id" : 10,
"name" : "Customer support call",
"duration" : 3,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-phone"
},
{
"id" : 11,
"name" : "Angular bug fix",
"duration" : 3,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-bug"
},
{
"id" : 12,
"name" : "React feature fix",
"duration" : 2,
"durationUnit" : "h",
"iconCls" : "b-fa b-fa-fw b-fa-cog"
}
]
}
}
In your crudManager you load that data
const crudManager = new CrudManager({
autoLoad : true,
// This config enables response validation and dumping of found errors to the browser console.
// It's meant to be used as a development stage helper only so please set it to false for production systems.
validateResponse : true,
eventStore : {
storeClass : TaskStore
},
resourceStore : {
modelClass : CustomResourceModel
},
transport : {
load : {
url : 'data/data.json'
}
}
});
And now you'd add the crudStore
crudManager.addCrudStore([{
id : 'unplannedTasks', // same key from data
modelClass : Task,
objectify : false
}]);
And then you'd need to get the last store (your custom store)
const customStore = crudManager.stores[crudManager.stores.length - 1].store;
And then use it inside of the unplanned grid
const unplannedGrid = new UnplannedGrid({
ref : 'unplanned',
appendTo : 'main',
title : 'Unplanned Tasks',
collapsible : true,
flex : '0 0 300px',
ui : 'toolbar',
// Schedulers stores are contained by a project, pass it to the grid to allow it to access them
project : schedule.project,
store : customStore
});
And you'll be good to go.
Here is what the whole code looks like all together
class CustomResourceModel extends ResourceModel {
static get $name() {
return 'CustomResourceModel';
}
static get fields() {
return [
// Do not persist `cls` field because we change its value on dragging unplanned resources to highlight the row
{ name : 'cls', persist : false }
];
}
}
const crudManager = new CrudManager({
autoLoad : true,
// This config enables response validation and dumping of found errors to the browser console.
// It's meant to be used as a development stage helper only so please set it to false for production systems.
validateResponse : true,
eventStore : {
storeClass : TaskStore
},
resourceStore : {
modelClass : CustomResourceModel
},
transport : {
load : {
url : 'data/data.json'
}
}
});
let schedule = new Schedule({
ref : 'schedule',
insertFirst : 'main',
startDate : new Date(2025, 11, 1, 8),
endDate : new Date(2025, 11, 1, 18),
flex : 4,
crudManager,
tbar : [
'Schedule view',
'->',
{ type : 'viewpresetcombo' },
{
type : 'button',
toggleable : true,
icon : 'b-fa-calendar',
pressedIcon : 'b-fa-calendar-check',
text : 'Automatic rescheduling',
tooltip : 'Toggles whether to automatically reschedule overlapping tasks',
cls : 'reschedule-button',
onToggle({ pressed }) {
schedule.autoRescheduleTasks = pressed;
}
},
{
type : 'buttonGroup',
toggleGroup : true,
items : [
{
icon : 'b-fa-fw b-fa-arrows-alt-v',
pressed : 'up.isVertical',
tooltip : 'Vertical mode',
schedulerConfig : {
mode : 'vertical',
subGridConfigs : {
locked : {
minWidth : 100,
flex : null
}
}
}
},
{
icon : 'b-fa-fw b-fa-arrows-alt-h',
pressed : 'up.isHorizontal',
tooltip : 'Horizontal mode',
schedulerConfig : {
mode : 'horizontal'
}
}
],
onAction({ source : button }) {
const newConfig = { ...schedule.initialConfig, ...button.schedulerConfig };
// Recreate the scheduler to switch orientation
schedule.destroy();
schedule = new Schedule(newConfig);
// Provide drag helper a reference to the new instance
drag.schedule = schedule;
}
}
]
});
crudManager.addCrudStore([{
id : 'unplannedTasks',
modelClass : Task,
objectify : false
}]);
new Splitter({
appendTo : 'main'
});
const customStore = crudManager.stores[crudManager.stores.length - 1].store;
const unplannedGrid = new UnplannedGrid({
ref : 'unplanned',
appendTo : 'main',
title : 'Unplanned Tasks',
collapsible : true,
flex : '0 0 300px',
ui : 'toolbar',
// Schedulers stores are contained by a project, pass it to the grid to allow it to access them
project : schedule.project,
store : customStore
});
const drag = new Drag({
grid : unplannedGrid,
schedule,
constrain : false,
outerElement : unplannedGrid.element
});
schedule.assignmentStore.on({
// When a task is unassigned move it back to the unplanned tasks grid
remove({ records }) {
records.forEach(({ event }) => {
schedule.eventStore.remove(event);
unplannedGrid.store.add(event);
});
},
thisObj : this
});
You can test this with our drag-from-grid
demo locally
Here is the result
Hope this helps
Best regards,
Tasnim