Our pure JavaScript Scheduler component


Post by oleh »

Ok, thanks anyway!


Post by oleh »

Hello again!
We managed to write our own Angular wrapper for the Scheduler. Basically it is a slightly modified copy of your wrapper, but we import everything from js files instead of @bryntum/scheduler/scheduler.lite.umd.js

It works fine, but we have a strange issue that only appears when our Angular app is built in production mode: when a user double-clicks the timeline to zoom in, or zooms in with the mouse wheel, this error appears: ERROR TypeError: Cannot set property tickWidth of [object Object] which has only a getter
Image

In TimeLineZoomable.js
Image
newPreset.setData is called to set tickWidth. But it appears that newPreset.data is not a raw data, but the Model class, where tickWidth is a getter. Moreover, the newPreset.data.data also can be not a raw data object, but a Model:
Image

We are not sure how it can be, and why it happens only in production mode, but maybe you have some ideas about it, where can it happen, that Model.data becomes a Model?

Our wrapper component (some inputs/outputs are cut off):

/* eslint-disable @angular-eslint/no-output-on-prefix */
/**
 * Angular wrapper for Bryntum SchedulerBase
 */
import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  Output,
  OnChanges,
  OnDestroy,
  OnInit,
  SimpleChanges,
  ViewEncapsulation
} from '@angular/core';
import type { SchedulerConfig } from '@bryntum/scheduler/scheduler.lite.umd.js';
import { bryntumConfigs, bryntumConfigsOnly, bryntumEvents, bryntumProps } from './component-fields';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const SchedulerBase = require('@bryntum/scheduler/source/lib/Scheduler/view/SchedulerBase').default;
// eslint-disable-next-line @typescript-eslint/no-var-requires
const WrapperHelper = require('@bryntum/scheduler-angular/esm2015/lib/wrapper.helper').default;

type Indexable = Record<string, any>;

@Component({
  selector: 'scheduler-wrapper',
  template: '',
  styleUrls: ['scheduler-wrapper.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class SchedulerWrapperComponent implements OnDestroy, OnInit, OnChanges, Indexable {
  //features
  @Input() cellEditFeature: any;
  ...
  @Input() treeGroupFeature: any;
  //props
  @Input() alignSelf: any;
  ...
  @Input() zoomLeve: any;
  //config
  @Input() adopt: any;
  ...
  @Input() zoomOnTimeAxisDoubleClic: any;

  // events
  @Output() onAfterDependencyCreateDrop = new EventEmitter<any>();
  ...
  @Output() onToolClick = new EventEmitter<any>();



  config: Partial<SchedulerConfig> = {
    adopt: this.elementRef.nativeElement,
    listeners: {},
    features: {}
  };

  instance: typeof SchedulerBase;

  constructor(private readonly elementRef: ElementRef) {}

  ngOnInit() {
    for (const property of bryntumConfigs.filter((property_) => property_ in this)) {
      WrapperHelper.applyPropValue(this.config, property, this[property]);
      if (['features', 'config'].includes(property)) {
        WrapperHelper.devWarningConfigProp('Scheduler', property);
      }
    }
    for (const event of bryntumEvents.filter((ev) => this[ev] && this[ev].observers.length > 0)) {
      const uncapitalize = (string_: string) => string_.charAt(0).toLowerCase() + string_.slice(1),
        eventName = (string_: string) => uncapitalize(string_.slice(2));
      (this.config.listeners as Indexable)[eventName(event)] = (e: unknown) => this[event].emit(e);
    }
    this.instance = new SchedulerBase(this.config);
  }

  ngOnChanges(changes: SimpleChanges) {
    const { instance } = this;
    if (!instance) {
      return;
    }
    // Iterate over all changes
    for (const [property, change] of Object.entries(changes)) {
      const newValue = change.currentValue;
      if (bryntumProps.includes(property)) {
        WrapperHelper.applyPropValue(instance, property, newValue, false);
        if (bryntumConfigsOnly.includes(property)) {
          WrapperHelper.devWarningUpdateProp('Scheduler', property);
        }
      }
    }
  }

  ngOnDestroy() {
    this.instance && this.instance.destroy && this.instance.destroy();
  }
}

Post by alex.l »

Very hard to say without full context.
Could you please attach a runnable app with steps to reproduce the problem? I am not sure how to use this wrapper and how did you use that in your app.
Please make sure you used latest released code base.

All the best,
Alex


Post by oleh »

The issue seems to be in Scheduler\view\mixin\TimelineViewPresets.js:
Image

preset.isViewPreset is always undefined in production mode, and by searching I cannot see where isViewPreset property is assigned. If I replace the code with

if (!preset.data) {

, everything works fine. Maybe it's opt out in prod mode...

I will try to make an app when I have some more free time.


Post by alex.l »

Thank you, for now I can't share any good ideas, unfortunately

All the best,
Alex


Post by oleh »

Hi

Attached is the app to reproduce the issue.
It has our Angular wrapper example app.

    • run 'yarn'
    • run 'yarn nx serve test --prod'
    • go to localhost:4200
    • zoom in/out with Ctrl+Mouse wheel or by doubleclicking the timeline
    • observe the errors in the console Image
    • you can fix the errors by uncommenting line 516 in \apps\test\src\app\scheduler-wrapper\scheduler-wrapper.component.ts file
    • run 'yarn nx serve test' - the error is not here anymore in dev mode
Attachments
scheduler-test.zip
(192.75 KiB) Downloaded 41 times

Post by alex.l »

Hi oleh,

We tried our best, but It's pretty hard to debug it. and since the problem happened after you modified our source code, I am afraid we cannot give you solution in bounds of forum support. Our forum rules here: viewtopic.php?t=772

We have this feature resolved https://github.com/bryntum/support/issues/2786 and aimed to be released in 5.1.0
I cannot provide you dates when it will be available, I will back with update when I have more info about that.

All the best,
Alex


Post by alex.l »

We plan to release 5.1.0-alpha-1 in next days, on this or next week.

All the best,
Alex


Post by oleh »

Great news, thank you!
As I see from the Github issue title, we won't need to have own wrapper anymore, is it correct?


Post by alex.l »

Yes, that's the point :)

All the best,
Alex


Post Reply