Our pure JavaScript Scheduler component


Post by shimnx »

I need to add these config into my Angular project.I have seen the JS demo and copy it to Angular project,but the grammar is incorrect.And I haven't seen these methids in the Angular demos in
the official documentation.
https://www.bryntum.com/examples/scheduler/vertical/


Post by shimnx »

The following are my codes.

import { Scheduler, StringHelper } from '@bryntum/scheduler/scheduler.lite.umd.js';
import { DateHelper } from '@bryntum/scheduler/scheduler.lite.umd.js';
export const scheduler1Config = {
    mode: 'vertical',
    eventColor: null,
    resourceImagePath: 'assets/users/',
    columns: [
        { type: 'resourceInfo', text: 'Staff', field: 'name', width: 150 },
        {
            text: 'Task color',
            field: 'eventColor',
            width: 90,
            htmlEncode: false,
            renderer: ({ record }) => `<div class="color-box b-sch-${record.eventColor}"></div>${StringHelper.capitalize(record.eventColor)}`,
            editor: {
                type: 'combo',
                items: Scheduler.eventColors,
                editable: false,
                listItemTpl: (item: any) => `<div class="color-box b-sch-${item.value}"></div><div>${item.value}</div>`
            }
        }
    ],

timeRangesFeature: {
    narrowThreshold: 10
},

crudManager: {
    autoLoad: true,
    transport: {
        load: {
            url: 'assets/data/data.json'
        }
    },
    // This config enables responses 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
},

barMargin: 5,
rowHeight: 50,

startDate: new Date(2017, 1, 7, 8),
endDate: new Date(2017, 1, 7, 18),
viewPreset: 'hourAndDay',
resourceMargin: 5,
eventStyle: 'colored',
tickSize: 80,
features: {
    filterBar: true,
    // required to filterable on columns work
    resourceTimeRanges: true,
    timeRanges: {
        enableResizing: true,
        showCurrentTimeLine: true
    },
    summary: {
        disabled: true,
        renderer: function renderer(_ref) {
            var events = _ref.events;
            return events.length;
        },
        verticalSummaryColumnConfig: {
            text: 'Summary'
        }
    }
},
resourceColumns: {
    columnWidth: 140 //,
    //headerRenderer : ({ resourceRecord }) => StringHelper.xss`${resourceRecord.id} - ${resourceRecord.name}`

},
verticalTimeAxisColumn: {
    filterable: {
        // filter configuration
        filterField: {
            // define the configuration for the filter field
            type: 'text',
            // type of the field rendered for the filter
            placeholder: 'Filter events',
            onChange: function onChange(_ref2) {
                var value = _ref2.value;
                // on change of the field, filter the event store

            }
        }
    }
},
subGridConfigs: {
    locked: {
        // Wide enough to not clip tick labels for all the zoom levels.
        width: 115
    }
},
tbar: [{
    type: 'date',
    value: 'up.startDate',
    step: '1d',
    onChange: function onChange(_ref4) {
      var value = _ref4.value;
      // Preserve time, only changing "day"
      var diff = DateHelper.diff(DateHelper.clearTime(scheduler1Config.startDate), value, 'days');
      scheduler1Config.startDate = DateHelper.add(scheduler1Config.startDate, diff, 'days');
    }
  }, {
    type: 'button',
    id: 'fitButton',
    text: 'Fit',
    icon: 'b-fa-arrows-alt-h',
    menu: {
      items: {
        none: {
          text: 'No fit',
          checked: false,
          //!scheduler.resourceColumns.fitWidth && !scheduler.resourceColumns.fillWidth,
          closeParent: true
        },
        fill: {
          text: 'Fill width',
          checked: 'up.resourceColumns.fillWidth',
          closeParent: true
        },
        fit: {
          text: 'Fit width',
          checked: 'up.resourceColumns.fitWidth',
          closeParent: true
        }
      },
      onItem: function onItem(_ref5) {
        var item = _ref5.source;
        item.owner.widgetMap.none.checked = item.ref === 'none';
        (scheduler1Config.resourceColumns as any).fillWidth = item.owner.widgetMap.fill.checked = item.ref === 'fill';
        (scheduler1Config.resourceColumns as any).fitWidth = item.owner.widgetMap.fit.checked = item.ref === 'fit';
        (scheduler1Config.resourceColumns as any).fitWidth = item.ref === 'fit';
      }
    }
  }, {
    type: 'button',
    text: 'Layout',
    icon: 'b-fa-layer-group',
    menu: {
      items: {
        none: {
          text: 'Overlap',
          checked: false,
          closeParent: true
        },
        pack: {
          text: 'Pack',
          checked: true,
          closeParent: true
        },
        mixed: {
          text: 'Mixed',
          checked: false,
          closeParent: true
        }
      },
      onItem: function onItem(_ref6) {
        var item = _ref6.source;
        var _item$owner$widgetMap = item.owner.widgetMap,
            none = _item$owner$widgetMap.none,
            pack = _item$owner$widgetMap.pack,
            mixed = _item$owner$widgetMap.mixed;
        none.checked = item.ref === 'none';
        pack.checked = item.ref === 'pack';
        mixed.checked = item.ref === 'mixed';
        scheduler1Config.eventLayout = item.ref;
      }
    }
  }, {
    type: 'button',
    text: 'Sizing',
    icon: 'b-fa-expand-arrows-alt',
    menu: {
      columnWidth: {
        type: 'slider',
        text: 'Column width',
        showValue: true,
        min: 50,
        max: 200,
        value: 'up.resourceColumnWidth',
        onInput: function onInput(_ref7) {
          var value = _ref7.value;
          var fitWidgetMap = scheduler1Config.widgetMap.fitButton.menu.widgetMap || {},
              fitNoneButton = fitWidgetMap.none,
              fitFillButton = fitWidgetMap.fill,
              fitFitButton = fitWidgetMap.fit;

          if (fitNoneButton) {
            fitNoneButton.checked = true;
            fitFillButton.checked = false;
            fitFitButton.checked = false;
          }

          scheduler1Config.resourceColumns.fitWidth = scheduler1Config.resourceColumns.fillWidth = null;
          scheduler1Config.resourceColumns.columnWidth = value;
        }
      },
      tickHeight: {
        type: 'slider',
        text: 'Tick height',
        showValue: true,
        min: 20,
        style: 'margin-top: .5em',
        value: 'up.tickSize',
        onInput: function onInput(_ref8) {
          var value = _ref8.value;
          // To allow ticks to not fill height
          scheduler1Config.suppressFit = true; // Set desired size

          scheduler1Config.tickSize = value;
        }
      },
      barMargin: {
        type: 'slider',
        text: 'Bar margin',
        showValue: true,
        min: 0,
        max: 10,
        style: 'margin-top: .5em',
        value: 'up.barMargin',
        onInput: function onInput(_ref9) {
          var value = _ref9.value;
          scheduler1Config.barMargin = value;
        }
      },
      resourceMargin: {
        type: 'slider',
        text: 'Resource margin',
        showValue: true,
        min: 0,
        max: 10,
        style: 'margin-top: .5em',
        value: 'up.resourceMargin',
        onInput: function onInput(_ref10) {
          var value = _ref10.value;
          scheduler1Config.resourceMargin = value;
        }
      }
    }
  }, {
    type: 'button',
    text: 'Show summary',
    toggleable: true,
    icon: 'b-fa-table',
    onToggle: function onToggle(_ref11) {
      var pressed = _ref11.pressed;
      scheduler1Config.features.summary.disabled = !pressed;
    }
  }]

};

Post by shimnx »

<bryntum-scheduler
    #scheduler1
    [tbar] = "schedulerConfig.tbar"
    [features] = "schedulerConfig.features"
    [mode] = "schedulerConfig.mode"
    [barMargin]         = "(store.select('barMargin') | async).barMargin"
    [columns]           = "schedulerConfig.columns"
    [crudManager]       = "schedulerConfig.crudManager"
    [endDate]           = "schedulerConfig.endDate"
    [eventColor]        = "schedulerConfig.eventColor"
    [resourceImagePath] = "schedulerConfig.resourceImagePath"
    [rowHeight]         = "schedulerConfig.rowHeight"
    [startDate]         = "schedulerConfig.startDate"
    [timeRangesFeature] = "schedulerConfig.timeRangesFeature"
    [viewPreset]        = "schedulerConfig.viewPreset"
</bryntum-scheduler>


Post by saki »

It looks good except features because we recommend to use them individually with the Feature suffix. In this example it would be:

[filterBarFeature] = "schedulerConfig.features.filterBar"
[resourceTimeRangesFeature] = "schedulerConfig.features.resourceTimeRanges"
// ... etc

Should you still be in troubles, post please a complete code that we can run, investigate and debug.


Post by shimnx »

Now these buttons are displayed but clicking toggle doesn't work

<bryntum-scheduler
    #scheduler1
    [tbar] = "schedulerConfig.tbar"
    [features] = "schedulerConfig.features"
    [mode] = "schedulerConfig.mode"
    [barMargin]         = "(store.select('barMargin') | async).barMargin"
    [columns]           = "schedulerConfig.columns"
    [crudManager]       = "schedulerConfig.crudManager"
    [endDate]           = "schedulerConfig.endDate"
    [eventColor]        = "schedulerConfig.eventColor"
    [resourceImagePath] = "schedulerConfig.resourceImagePath"
    [rowHeight]         = "schedulerConfig.rowHeight"
    [startDate]         = "schedulerConfig.startDate"
    [timeRangesFeature] = "schedulerConfig.timeRangesFeature"
    [viewPreset]        = "schedulerConfig.viewPreset"
    [filterBarFeature] = "schedulerConfig.features.filterBar"
    [resourceTimeRangesFeature] = "schedulerConfig.features.resourceTimeRanges"
</bryntum-scheduler>
import { Component, OnDestroy, AfterViewInit, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { ActivatedRoute, Event, NavigationEnd, NavigationStart, Router } from '@angular/router';
import { BryntumSchedulerComponent } from '@bryntum/scheduler-angular';
import { Scheduler } from '@bryntum/scheduler/scheduler.lite.umd.js';
import { scheduler1Config } from './scheduler1.config';

@Component({
    selector    : 'app-scheduler1',
    templateUrl : './scheduler1.component.html'
})
export class Scheduler1Component implements AfterViewInit, OnDestroy {

schedulerConfig: any = scheduler1Config;
scheduler: Scheduler;

@ViewChild(BryntumSchedulerComponent, { static : true }) schedulerComponent: BryntumSchedulerComponent;

constructor(
    public router: Router,
    public route: ActivatedRoute,
    public store: Store<{ barMargin: { barMargin: number } }>
) {
}

ngAfterViewInit(): void {
    // console.log('initialize scheduler 1');
    this.scheduler = this.schedulerComponent.instance;

    const saveState = (event: Event) => {
        console.log(event)
        if (event instanceof NavigationStart) {
            if (this.router.isActive(this.route.routeConfig.path, true)) {
                this.scheduler.storeScroll();
            }
        }
        else if (event instanceof NavigationEnd) {
            if (this.router.isActive(this.route.routeConfig.path, true)) {
                this.scheduler.restoreScroll();
            }
        }
    };

    this.router.events.subscribe(saveState);
}

ngOnDestroy(): void {
    // console.log('destroying scheduler1');
}

}
import { Scheduler, StringHelper } from '@bryntum/scheduler/scheduler.lite.umd.js';
import { DateHelper } from '@bryntum/scheduler/scheduler.lite.umd.js';
export const scheduler1Config = {
    mode: 'vertical',
    eventColor: null,
    resourceImagePath: 'assets/users/',
    columns: [
        { type: 'resourceInfo', text: 'Staff', field: 'name', width: 150 },
        {
            text: 'Task color',
            field: 'eventColor',
            width: 90,
            htmlEncode: false,
            renderer: ({ record }) => `<div class="color-box b-sch-${record.eventColor}"></div>${StringHelper.capitalize(record.eventColor)}`,
            editor: {
                type: 'combo',
                items: Scheduler.eventColors,
                editable: false,
                listItemTpl: (item: any) => `<div class="color-box b-sch-${item.value}"></div><div>${item.value}</div>`
            }
        }
    ],

timeRangesFeature: {
    narrowThreshold: 10
},

crudManager: {
    autoLoad: true,
    transport: {
        load: {
            url: 'assets/data/data.json'
        }
    },
    // This config enables responses 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
},

barMargin: 5,
rowHeight: 50,

startDate: new Date(2017, 1, 7, 8),
endDate: new Date(2017, 1, 7, 18),
viewPreset: 'hourAndDay',
resourceMargin: 5,
eventStyle: 'colored',
tickSize: 80,
features: {
    filterBar: true,
    // required to filterable on columns work
    resourceTimeRanges: true,
    timeRanges: {
        enableResizing: true,
        showCurrentTimeLine: true
    },
    summary: {
        disabled: true,
        renderer: function renderer(_ref) {
            var events = _ref.events;
            return events.length;
        },
        verticalSummaryColumnConfig: {
            text: 'Summary'
        }
    }
},
resourceColumns: {
    columnWidth: 140 //,
    //headerRenderer : ({ resourceRecord }) => StringHelper.xss`${resourceRecord.id} - ${resourceRecord.name}`

},
verticalTimeAxisColumn: {
    filterable: {
        // filter configuration
        filterField: {
            // define the configuration for the filter field
            type: 'text',
            // type of the field rendered for the filter
            placeholder: 'Filter events',
            onChange: function onChange(_ref2) {
                var value = _ref2.value;
                // on change of the field, filter the event store

            }
        }
    }
},
subGridConfigs: {
    locked: {
        // Wide enough to not clip tick labels for all the zoom levels.
        width: 115
    }
},
tbar: [{
    type: 'date',
    value: 'up.startDate',
    step: '1d',
    onChange: function onChange(_ref4) {
      var value = _ref4.value;
      // Preserve time, only changing "day"
      var diff = DateHelper.diff(DateHelper.clearTime(scheduler1Config.startDate), value, 'days');
      scheduler1Config.startDate = DateHelper.add(scheduler1Config.startDate, diff, 'days');
    }
  }, {
    type: 'button',
    id: 'fitButton',
    text: 'Fit',
    icon: 'b-fa-arrows-alt-h',
    menu: {
      items: {
        none: {
          text: 'No fit',
          checked: false,
          //!scheduler.resourceColumns.fitWidth && !scheduler.resourceColumns.fillWidth,
          closeParent: true
        },
        fill: {
          text: 'Fill width',
          checked: 'up.resourceColumns.fillWidth',
          closeParent: true
        },
        fit: {
          text: 'Fit width',
          checked: 'up.resourceColumns.fitWidth',
          closeParent: true
        }
      },
      onItem: function onItem(_ref5) {
        var item = _ref5.source;
        item.owner.widgetMap.none.checked = item.ref === 'none';
        (scheduler1Config.resourceColumns as any).fillWidth = item.owner.widgetMap.fill.checked = item.ref === 'fill';
        (scheduler1Config.resourceColumns as any).fitWidth = item.owner.widgetMap.fit.checked = item.ref === 'fit';
        (scheduler1Config.resourceColumns as any).fitWidth = item.ref === 'fit';
      }
    }
  }, {
    type: 'button',
    text: 'Layout',
    icon: 'b-fa-layer-group',
    menu: {
      items: {
        none: {
          text: 'Overlap',
          checked: false,
          closeParent: true
        },
        pack: {
          text: 'Pack',
          checked: true,
          closeParent: true
        },
        mixed: {
          text: 'Mixed',
          checked: false,
          closeParent: true
        }
      },
      onItem: function onItem(_ref6) {
        var item = _ref6.source;
        var _item$owner$widgetMap = item.owner.widgetMap,
            none = _item$owner$widgetMap.none,
            pack = _item$owner$widgetMap.pack,
            mixed = _item$owner$widgetMap.mixed;
        none.checked = item.ref === 'none';
        pack.checked = item.ref === 'pack';
        mixed.checked = item.ref === 'mixed';
        (scheduler1Config as any).eventLayout = item.ref;
      }
    }
  }, {
    type: 'button',
    text: 'Sizing',
    icon: 'b-fa-expand-arrows-alt',
    menu: {
      columnWidth: {
        type: 'slider',
        text: 'Column width',
        showValue: true,
        min: 50,
        max: 200,
        value: 'up.resourceColumnWidth',
        onInput: function onInput(_ref7) {
          var value = _ref7.value;
          var fitWidgetMap = (scheduler1Config as any).widgetMap.fitButton.menu.widgetMap || {},
              fitNoneButton = fitWidgetMap.none,
              fitFillButton = fitWidgetMap.fill,
              fitFitButton = fitWidgetMap.fit;

          if (fitNoneButton) {
            fitNoneButton.checked = true;
            fitFillButton.checked = false;
            fitFitButton.checked = false;
          }

          (scheduler1Config.resourceColumns as any).fitWidth = (scheduler1Config.resourceColumns as any).fillWidth = null;
          scheduler1Config.resourceColumns.columnWidth = value;
        }
      },
      tickHeight: {
        type: 'slider',
        text: 'Tick height',
        showValue: true,
        min: 20,
        style: 'margin-top: .5em',
        value: 'up.tickSize',
        onInput: function onInput(_ref8) {
          var value = _ref8.value;
          // To allow ticks to not fill height
          (scheduler1Config as any).suppressFit = true; // Set desired size

          scheduler1Config.tickSize = value;
        }
      },
      barMargin: {
        type: 'slider',
        text: 'Bar margin',
        showValue: true,
        min: 0,
        max: 10,
        style: 'margin-top: .5em',
        value: 'up.barMargin',
        onInput: function onInput(_ref9) {
          var value = _ref9.value;
          scheduler1Config.barMargin = value;
        }
      },
      resourceMargin: {
        type: 'slider',
        text: 'Resource margin',
        showValue: true,
        min: 0,
        max: 10,
        style: 'margin-top: .5em',
        value: 'up.resourceMargin',
        onInput: function onInput(_ref10) {
          var value = _ref10.value;
          scheduler1Config.resourceMargin = value;
        }
      }
    }
  }, {
    type: 'button',
    text: 'Show summary',
    toggleable: true,
    icon: 'b-fa-table',
    onToggle: function onToggle(_ref11) {
      var pressed = _ref11.pressed;
      scheduler1Config.features.summary.disabled = !pressed;
    }
  }]

};

Post by saki »

We need to analyze what's happening here:

  1. handlers/values that are strings, such as 'up.resourceColumns.fillWidth' are actually formulas that tell how to find the actual handler/value. up means to ho up the container chain until resourceColumns are found and then take the checked state from fillWidth

  2. You define it in schedulerConfig and you pass it to the wrapper. But wrapper only processes known properties as listed in https://bryntum.com/docs/scheduler/#Scheduler/view/Scheduler – others are ignored.

Therefore, listeners/values must be available and reachable for tbar to work.

Post please your code in a runnable form and I'll modify it for one of them to work, if you're still in troubles.


Post Reply