Dragged element is not rendered correctly when `constrainDragToTimeline` disabled
Test case BryntumDragIssue (1).zip If not runnable with yarn, change version in package.json for yarn from 4.0.4 to 1.2.0 (or whatever you have locally)
Worked when constraints enabled, but doesn't when it disabled. An app attached is on Angular, but it is required to be reviewed by Core developer, not Angular.
We are using Brynutm Scheduler 6.0.1 with Angular 17 and have implemented the scheduler with our own renderer component:
const EVENT_COMPONENT_SELECTOR = getComponentSelector(SchedulingBoardEventComponent);
export const eventRendererConfig = {
eventColor: null,
eventRenderer: (): string => `<${EVENT_COMPONENT_SELECTOR} />`,
onRenderEvent: ({ eventRecord, element: parentElement }): void => {
const data = (eventRecord as SchedulingBoardModel<SchedulingBoardItem>).getData('data');
const element = findFirstElementOrThrow<SchedulingBoardComponentWithData<SchedulingBoardItem>>(
EVENT_COMPONENT_SELECTOR,
parentElement,
);
element.data = data;
},
} satisfies SchedulerConfig;
Everything is working fine, until we deactivate constrainDragToTimeline and set it to false to be able to drag between to connected schedulers.
Our custom renderer component has the following template:
<div *ngIf="data; else dragCreationElement">
<app-scheduling-board-creation-card
*ngIf="data.modelType === SchedulingBoardItemTypeEnum.CREATION"
[subject]="data.subject"
[color]="data.color"
/>
<app-work-order-card-scheduling
*ngIf="data.modelType === SchedulingBoardItemTypeEnum.WORK_ORDER"
[workOrder]="data"
[hasNoBackground]="!(showFullyColoredCards$ | async)"
/>
</div>
<ng-template #dragCreationElement>
<app-scheduling-board-creation-card />
</ng-template>
and once we set constrainDragToTimeline to false and start to drag, it is always rendering the fallback component
Here is a screen shot of the scheduling board while dragging:
Screenshot of the Dom element while dragging: https://drive.google.com/file/d/1RbabIV5pWADdbZDu65wCEIrl1bzPgyJm/view?usp=drive_link
Any idea, what we can do to fix the rendering while dragging?
Thank you!
pinged https://forum.bryntum.com/viewtopic.php?p=155941#p155941
Here is the summary of what is happening here:
- If
constrainDragToTimelineisfalsethen a clone (a proxy) is created for dragging and the proxy is dragged while the original element is hidden. - The event is rendered by means of a Custom Element, which is defined in the Angular application and created by
eventRendererfunction. - In the provided application the Custom Element receives
Objectas an attribute but the CEs, similarly to the HTML tags, can only receive string values as attributes. Although the "object" approach initially works while creating the CEs, there is no way how the resulting markup could be cloned with complex attributes, therefore the content of the event changes while dragging, not receiving the expected data object. - The solution could be either a) spread the data object into individual attributes with string values (preferred because changes can be easily observed and reacted to) or b) serialize data object into JSON string (which could be easier if there's no interaction of the user with the rendered CEs).
The provided demo application can be made working by the following:
- Change the
SchedulingBoardEventComponentto read the following:
import {Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-scheduling-board-event',
templateUrl: './scheduling-board-event.component.html',
styleUrl: './scheduling-board-event.component.scss',
})
export class SchedulingBoardEventComponent implements OnInit {
@Input() text: string | undefined;
constructor() {}
ngOnInit() {
console.log('SchedulingBoardEventComponent initialized');
}
}
Template:
<ng-container *ngIf="text; else dragCreationElement">
<div>
<p>{{text}}</p>
</div>
</ng-container>
<ng-template #dragCreationElement>
<div>
<p>Creation Card</p>
</div>
</ng-template>
- Change the
eventRendererinapp.config.tsto read the following:
eventRenderer: ({ eventRecord, resourceRecord, renderData }): string => {
return `<app-scheduling-board-event text="${eventRecord.name}"></app-scheduling-board-event>`;
},
The conclusion is that this is not a bug that can be solved by the Bryntum code but it is a limitaion of Custom Elements which can be worked around easily.
Closing the ticket.
I'm re-opening the ticket because the true reason is, as I wrote on Slack to @isglass:
In EventDrag feature we delete original and create a new element when constrainDragToTimeline is false. Normally, it’s not a problem but in Angular, where we use custom elements as renderers it fails to render the proper content while dragging. A user is complaining about that here: https://forum.bryntum.com/viewtopic.php?t=30466
The thing is that if we created the proxy with simple element.cloneNode(true) then the content is preserved; I have tried it and it would resolve the issue.
At least we should investigate whether it is possible to use cloneNode universally. At least we should add an opt-out config option as the user suggests in the forum thread.
This seems to be a shortcoming when using custom elements. As discussed in short with @jsakalos, a more "Anglar-ish" solution would be to use createComponent instead. I'll post a working code sample to the forum thread; also see changes below.
We will discuss implications, and evaluate the general viability of this approach after our end-of-year break.
app.component.ts
private viewContainerRef: ViewContainerRef = inject(ViewContainerRef);
onRenderEvent ({ eventRecord }: { eventRecord: any }) {
const data = (eventRecord as any).data;
const componentRef = this.viewContainerRef.createComponent(SchedulingBoardEventComponent);
componentRef.setInput('data', data);
componentRef.changeDetectorRef.detectChanges();
}
app.component.html
Remove [eventRenderer] binding from schedulers
app.config.ts
Remove eventRenderer from scheduler configs
app.module.ts
Remove constructor (or the statement customElements.define(...))