- Home
- Forum
- React - EJ 2
- Paste appointment object to Schedule
Paste appointment object to Schedule
I'd like to use the copy & paste functionlity provided by the Schedule component. However due to my app running inside Microsoft Teams, direct access to the clipboard for pasting is not possible (copying works though).
That means I can get the copied value from the clipboard using the Teams SDK but don't know how to then paste it to the schedule. Is there a way around this or could this be logged as a feature request?
Thanks
Hi
Thomas Pentenrieder,
Greetings from Syncfusion Support.
Before proceeding, we require additional details regarding your reported query.
We have reviewed and validated your initial report, noting that you mentioned
your application is running inside Microsoft Teams. Please provide details on
how you are running the application, including a video demonstration of the
execution and the application file itself, to facilitate further investigation.
Don't hesitate to get in touch if you require further help or more information.
Regards,
Vijay
Hello,
the control is running as a Tab app inside Teams, see this video for demo:
https://www.youtube.com/watch?v=SEuS8X0gtUs
This means the app is inside an iframe where I don't have control over the
Hi
Thomas Pentenrieder,,
Microsoft
Teams apps in iframes face Clipboard API blocks due to Permissions Policy.
Developers lack direct <iframe> control to set
allow="clipboard-read; clipboard-write". Teams' iframe restrictions
prevent standard clipboard access. A workaround involves using a server-side
bot to facilitate clipboard actions, relaying content between user and app.
This maintains functionality without direct Clipboard API use. Alternate
solutions may involve custom messaging extensions, offering streamlined
copying.
https://stackoverflow.com/questions/76488175/possibility-to-enable-clipboard-api-in-microsoft-teams-app
https://techcommunity.microsoft.com/discussions/teamsdeveloper/how-can-i-get-clipboard-permission-in-teams-add-on-app/3613731
Don't hesitate to get in touch if you require further help or more information.
Regards,
Vijay
Please refer to my original question. I am able to get the value from the clipboard using Teams SDK. Even with your suggestion using a bot (?), how can I programatically pass the copied value to the Schedule so it will be correctly inserted into the selected cell / resource?
Also, the linked resources above are out of date.
Hi
Thomas Pentenrieder,
We
reviewed your query about programmatically inserting copied values into the
selected cell/resource in the Scheduler. The Scheduler provides built-in
methods for cut, copy, and paste operations. You can use these methods to
perform cut/copy/paste actions programmatically. Please refer to the
documentation linked below for details.
https://ej2.syncfusion.com/react/documentation/schedule/clipboard#cut-copy-and-paste-using-context-menu
Copy API: https://ej2.syncfusion.com/react/documentation/api/schedule/index-default#copy
Cut API: https://ej2.syncfusion.com/react/documentation/api/schedule/index-default#cut
Paste API: https://ej2.syncfusion.com/react/documentation/api/schedule/index-default#paste
Don't hesitate to get in touch if you require further help or more information.
Regards,
Vijay
No, I cannot use the built-in methods for paste, which is the whole point of this thread. Please look at the error message in post #1.
- I can copy an appointment using the built-in copy method
- I can not paste using the built-in paste method
- I can get the clipboard value programatically through the TeamsJs SDK
- I need a way to pass the copied value to the schedule, e.g. schedule.paste(targetElement, payload), but this is not supported. is there another way?
Hi
Thomas Pentenrieder,
We have checked and validated your reported query. The built-in paste relies on
clipboard access and won’t work in the Teams iframe. Instead, bypass the
clipboard and paste programmatically. Capture the target slot using
getCellDetails on the clicked cell (or via cellClick). Take your copied payload
(from TeamsJS), normalize its StartTime/EndTime to Date, compute the duration,
and create a new event aligned to the target slot, preserving duration and
mapping the resource. Use addEvent for “Copy→Paste,” and for “Cut→Paste,”
delete the original with deleteEvent before adding the adjusted event. Ensure
you generate a unique Id for the pasted copy and carry over flags like
IsAllDay/RecurrenceRule. This approach replicates paste behavior without
relying on clipboard APIs and works across views/resources.
|
import { createRoot } from 'react-dom/client'; import './index.css'; import * as React from 'react'; import { useRef } from 'react'; import { extend, closest, isNullOrUndefined, remove } from '@syncfusion/ej2-base'; import { ScheduleComponent, ViewsDirective, ViewDirective, Day, Week, WorkWeek, Month, TimelineViews, TimelineMonth, Resize, DragAndDrop, Inject } from '@syncfusion/ej2-react-schedule'; import { ContextMenuComponent } from '@syncfusion/ej2-react-navigations'; import * as dataSource from './datasource.json';
const ClipboardSchedule = () => { const scheduleObj = useRef<ScheduleComponent | null>(null); const menuObj = useRef<any>(null); let selectedTarget: HTMLElement | undefined; let targetElement: HTMLElement | undefined;
// Local clipboard (can also be set from TeamsJS payload) let clip: any = null; let isCut = false;
const data = extend([], (dataSource as any).scheduleData, null, true); const menuItems = [ { text: 'Cut Event', iconCss: 'e-icons e-cut', id: 'Cut' }, { text: 'Copy Event', iconCss: 'e-icons e-copy', id: 'Copy' }, { text: 'Paste', iconCss: 'e-icons e-paste', id: 'Paste' } ];
// Optional: call this when TeamsJS returns the copied payload // Ensure StartTime/EndTime are Date objects const setClipboardFromTeams = (payload: any) => { clip = { ...payload, StartTime: new Date(payload.StartTime), EndTime: new Date(payload.EndTime) }; isCut = false; };
const onContextMenuBeforeOpen = (args: any) => { const newEventElement = document.querySelector('.e-new-event'); if (newEventElement) remove(newEventElement as Element);
scheduleObj.current?.closeQuickInfoPopup(); targetElement = args.event.target as HTMLElement; if (closest(targetElement, '.e-contextmenu')) return;
selectedTarget = closest( targetElement, '.e-appointment,.e-work-cells,.e-vertical-view .e-date-header-wrap .e-all-day-cells,.e-vertical-view .e-date-header-wrap .e-header-cells' ) as HTMLElement;
if (isNullOrUndefined(selectedTarget)) { args.cancel = true; return; }
if (selectedTarget.classList.contains('e-appointment')) { menuObj.current.showItems(['Cut', 'Copy'], true); menuObj.current.hideItems(['Paste'], true); } else { menuObj.current.showItems(['Paste'], true); menuObj.current.hideItems(['Cut', 'Copy'], true); } };
const onMenuItemSelect = (args: any) => { const id = args.item.id; if (!scheduleObj.current) return;
switch (id) { case 'Copy': { // Get the event object from the clicked appointment element const evt = scheduleObj.current.getEventDetails(selectedTarget as Element); clip = { ...evt, StartTime: new Date(evt.StartTime), EndTime: new Date(evt.EndTime) }; isCut = false; break; } case 'Cut': { const evt = scheduleObj.current.getEventDetails(selectedTarget as Element); clip = { ...evt, StartTime: new Date(evt.StartTime), EndTime: new Date(evt.EndTime) }; isCut = true; break; } case 'Paste': { if (!clip || !targetElement) return;
// Determine the target cell details (time/resource) from the clicked cell const cellEl = closest(targetElement, '.e-work-cells, .e-all-day-cells') as HTMLElement; const cell = scheduleObj.current.getCellDetails(cellEl) as any; // { startTime, endTime, resource, groupIndex, ... }
const duration = clip.EndTime.getTime() - clip.StartTime.getTime(); const newStart = new Date(cell.startTime); const newEnd = new Date(newStart.getTime() + duration);
// Map resource field if you use grouping. Adjust 'ResourceId' to your model (e.g., OwnerId). const resourceId = cell?.resource?.data?.Id ?? clip.ResourceId;
const adjusted = { ...clip, Id: Date.now(), // ensure unique Id for copied items StartTime: newStart, EndTime: newEnd, ResourceId: resourceId };
if (isCut) { scheduleObj.current.deleteEvent(clip); // remove original scheduleObj.current.addEvent(adjusted); clip = null; isCut = false; } else { scheduleObj.current.addEvent(adjusted); } break; } } };
return ( <div className='schedule-control-section'> <div className='col-lg-12 control-section'> <div className='content-wrapper'> <div className='schedule-container'> <ScheduleComponent width='100%' height='550px' ref={scheduleObj} selectedDate={new Date(2021, 0, 10)} eventSettings={{ dataSource: data }} allowClipboard={false} // not using built-in paste showQuickInfo={false} > <ViewsDirective> <ViewDirective option='Week' /> <ViewDirective option='Day' /> <ViewDirective option='Month' /> <ViewDirective option='TimelineDay' /> <ViewDirective option='TimelineWeek' /> <ViewDirective option='TimelineWorkWeek' /> <ViewDirective option='TimelineMonth' /> </ViewsDirective> <Inject services={[Day, Week, WorkWeek, Month, TimelineViews, TimelineMonth, Resize, DragAndDrop]} /> </ScheduleComponent>
<ContextMenuComponent target='.e-schedule' items={menuItems} beforeOpen={onContextMenuBeforeOpen} select={onMenuItemSelect} cssClass='schedule-context-menu' ref={menuObj} /> </div> </div> </div> </div> ); };
export default ClipboardSchedule;
// Mount const root = createRoot(document.getElementById('sample')!); root.render(<ClipboardSchedule />); |
Don't hesitate to get in touch if you require further help or more information.
Regards,
Vijay
- 7 Replies
- 2 Participants
-
TP Thomas Pentenrieder
- Nov 8, 2025 02:44 PM UTC
- Nov 17, 2025 10:18 AM UTC