Retrieve cell data on event click

Hi there,

I need to retrieve the cell data on an event click in order to correctly position my custom quick info popup. Currently, the cellClick method provides the relevant element that I need (

). How do I get the cell td on an event click? Currently, the element that is retrieved is the div that holds the quick popup. Some additional information that may be helpful: I need the anchor element that is part of the document layout in order to render a Material UI Popper. 


<ScheduleComponent
width="auto"
height="60vh"
ref={scheduleObjRef}
eventSettings={{
dataSource: appointments,
fields: fieldsData,
template: renderEventTemplate,
}}
popupOpen={onPopupOpen}
created={onCreated}
eventClick={args => setAnchorEl(args.element)}
cellClick={args => setAnchorEl(args.element)}
>
<ViewsDirective>
<ViewDirective option="Day" isSelected />
<ViewDirective option="Week" />
ViewsDirective>
<Inject services={[Day, Week]} />
ScheduleComponent>

17 Replies

SK Satheesh Kumar Balasubramanian Syncfusion Team June 22, 2023 01:14 PM UTC

Hi Jessica,

You can retrieve the cell details of the clicked event using the below code snippets.


[index.js]

  const onEventClick = (args) => {
    let start = args.event.StartTime.getTime();
    let end = args.event.EndTime.getTime();
    let time =
      (scheduleObj.current.timeScale.interval /
        scheduleObj.current.timeScale.slotCount) *
      1000 *
      60;
    for (let i = start; i < end; i = i + time) {
      var cellDetails = scheduleObj.current.element.querySelector(
        '.e-work-cells[data-date="' + i + '"]'
      );
      console.log(cellDetails);
    }
  };

Regards,
Satheesh Kumar B


JN Jessica Newman June 22, 2023 01:26 PM UTC

There is not a more straightforward answer? I need to access the dom node that represents the time of the appointment. Seems like there should be a simple way to access this information instead of manipulating cell data.



JN Jessica Newman June 22, 2023 01:32 PM UTC

Additionally, with this response I am still getting the error that "The anchor element should be part of the document layout. Make sure the element is present in the document or that it's not display none." This is the element I get when selecting an event:  <td aria-selected="false" class="e-work-cells e-alternate-cells e-work-hours" data-date="1687365000000" data-group-index="0"></td>. 


Can you please provide a better solution? This is inhibiting the production of my application. Thank you.



VS Venkateshwaran Saravanakumar Syncfusion Team June 23, 2023 03:56 PM UTC

Hi Jessica,

Q1: I need to access the dom node that represents the time of the appointment
In cellClick and eventClick events of the Schedule we provided respective target elements and its related details. If you want the cell details in the eventClick event of the Schedule, we suggest you achieve that requirement using our previous solution at your application end like highlighted in the below code snippet. Since you want that cell detail to position your custom quick info popup. In the eventClick event of the Schedule you will get the clicked appointment element. So, we suggest you use the appointment element to position your custom quick info popup.

Sample: https://stackblitz.com/edit/react-schedule-get-cell-details-event-pygs3m?file=index.js

[index.js]

  const onEventClick = (args=> {

    // Use the appointment element present in the args element or use the cell queried in the targetCell variable

    console.log(args.element);

    var targetCell = scheduleObj.current.element.querySelector('.e-work-cells[data-date="' + args.event.StartTime.getTime() + '"]');

    console.log(targetCell);

  };


Q2: I am still getting the error that "The anchor element should be part of the document layout. Make sure the element is present in the document or that it's not display none."

We are unable to reproduce the issue at our end. We suspect that the issue is not related to the Schedule. We suggest you check the other customization that you have done in your project. If you still facing problem, share more details about the issue.

  • Schedule related code snippets or
  • Reproduce the issue in our shared sample
  • Issue reproducing video demo.

       

Regards,
Venkatesh



JN Jessica Newman June 26, 2023 10:55 PM UTC

Thank you! This ended up working as I expect it to. 

As a follow up, I have implemented the schedule component in another application where I have multiple resources. When I click on a cell to retrieve the appropriate <td></td> element in my popupOpen function via args.target, I am getting a similar warning that says "Node cannot be found in the current page" when I click on the element in the console. Do you have a similar solution? Thank you!


  1. {type: 'QuickInfo', cancel: false, data: {…}, target: td.e-work-cells.e-selected-cell, element: div.e-quick-popup-wrapper.e-popup-close.e-lib.e-popup.e-control, …}
    1. cancel: true
    2. name: "popupOpen"
    3. type: "QuickInfo"


function onPopupOpen(args) {
if (args.type === 'QuickInfo') {
setAnchorEl(args.element)
}
}


JN Jessica Newman June 28, 2023 03:21 PM UTC

Are you able to provide a resolution to this problem?



RV Ravikumar Venkatesan Syncfusion Team June 28, 2023 04:35 PM UTC

Hi Jessica,


Query: I am getting a similar warning that says "Node cannot be found in the current page" when I click on the element in the console

We checked your shared codes at our end in your shared code you are setting up args.cancel = true and using the args.element in the setAnchorEl method. The element in the args.element is the Schedule quick info popup. Since you are setting up args.cancel = true the popup element will be hidden in the DOM. We suspect that because of that you are facing your reported problem. We suggest you use the args.target(Contains clicked target cell element) instead of args.element in the setAnchorEl method. If you still facing the same problem, share the below details to proceed further.


  • The video demo shows the problem.
  • Issue reproducing code snippets or
  • Reproduce the issue in our shared sample or
  • Simple issue reproducing sample.


Regards,

Ravikumar Venkatesan



JN Jessica Newman July 3, 2023 01:04 PM UTC

Hi there, 

Here are additional code snippets to portray my issue. I need to pass in an anchor el to my QuickInfoPopup component, which renders a custom material UI popper component. When setting the anchorEl in the onPopupOpen function, I get an error in my console saying "The `anchorEl` prop provided to the component is invalid. The anchor element should be part of the document layout. Make sure the element is present in the document or that it's not display none." I used the solution provided above in a different scheduler component I rendered that did not have multiple resources and it worked perfectly, so I am wondering if that is one of the problems I am facing. If you are able to respond today, I would greatly appreciate it. Thank you. Let me know if you need additional information. 

import { useRef, useState, useEffect } from 'react';
import ResourceHeaderTemplate from './ResourceHeaderTemplate';
import EditorTemplate from './EditorTemplate';
import QuickInfoPopup from './QuickInfoPopup';
import EventTemplate from './EventTemplate';
import {
Inject,
ResourceDirective,
ResourcesDirective,
ScheduleComponent,
TimelineMonth,
TimelineViews,
ViewDirective,
ViewsDirective,
} from '@syncfusion/ej2-react-schedule';
import { Internationalization } from '@syncfusion/ej2-base';
import { QueryKeys } from '../../../service/queryClient';
import { useQuery, useQueryClient } from 'react-query';
import CenterBackdrop from '../../components/CenterBackdrop';

export default function Dashboard({ appBarHeight }) {
const [editorOpen, setEditorOpen] = useState(false);
const [popupOpen, setPopupOpen] = useState(false);
const [currentAppointment, setCurrentAppointment] = useState({});
const [currentClient, setCurrentClient] = useState({});
const [anchorEl, setAnchorEl] = useState(null);
const scheduleObjRef = useRef(null);
const queryClient = useQueryClient();
const instance = new Internationalization();
const { data: clients, isLoading, refetch } = useQuery(QueryKeys.CLIENTS);
const appointments = clients?.flatMap(client =>
client.appointments?.map(appointment => ({ ...appointment, ClientId: client.id })),
);

async function refreshClients() {
await queryClient.invalidateQueries(QueryKeys.CLIENTS);
await refetch();
}

const fieldsData = {
id: 'id',
subject: { name: 'name' },
isAllDay: { name: 'IsAllDay' },
location: { name: 'Location' },
description: { name: 'notes' },
startTime: { name: 'startTime' },
endTime: { name: 'endTime' },
startTimezone: { name: 'StartTimezone' },
endTimezone: { name: 'EndTimezone' },
};

useEffect(() => {
if (!popupOpen) {
setAnchorEl(null);
}
}, [popupOpen]);

function onCreated() {
const currentDate = new Date();
const currentHour = currentDate.getHours();
const newHour = currentHour === 1 ? 12 : currentHour - 1;

currentDate.setHours(newHour);
const newTime = instance.formatDate(currentDate, { skeleton: 'Hm' });
scheduleObjRef.current.scrollTo(newTime);
}

function renderEventTemplate(appointment) {
return <EventTemplate appointment={appointment} />;
}

function onPopupOpen(args) {
if (args.type === 'Editor') {
args.cancel = true;
if (args.data.drug) {
setEditorOpen(true);
setCurrentAppointment(args.data);
setCurrentClient(clients.find(c => c.id === args.data.ClientId));
}
} else if (args.type === 'DeleteAlert') {
args.cancel = true;
} else if (args.type === 'QuickInfo') {
args.cancel = true;
setPopupOpen(true);
setCurrentAppointment(args.data);
setCurrentClient(clients.find(c => c.id === args.data.ClientId));
handleSetAnchorEl(args);
}
}

//This is the solution implemented for me earlier in this thread. I will also need to attach the appropriate data-index-group. Please
//assist in the best way to do so.
function handleSetAnchorEl(args) {
let start = args.data.startTime.getTime();
let end = args.data.endTime.getTime();
let time = (scheduleObjRef.current.timeScale.interval / scheduleObjRef.current.timeScale.slotCount) * 1000 * 60;
for (let i = start; i < end; i += time) {
let cellDetails = scheduleObjRef.current.element.querySelector('.e-work-cells[data-date="' + i + '"]');
setAnchorEl(cellDetails);
}
}

return isLoading ? (
<CenterBackdrop />
) : (
<>
{editorOpen && (
<EditorTemplate
client={currentClient}
editorOpen={editorOpen}
setEditorOpen={setEditorOpen}
appointment={currentAppointment}
refreshClients={refreshClients}
/>
)}

{popupOpen && (
<QuickInfoPopup
appointment={currentAppointment}
popupOpen={popupOpen}
setPopupOpen={setPopupOpen}
client={currentClient}
refreshClients={refreshClients}
setEditorOpen={setEditorOpen}
setCurrentAppointment={setCurrentAppointment}
anchorEl={anchorEl}
/>
)}
<ScheduleComponent
width="auto"
height={`calc(100% - ${appBarHeight}px)`}
ref={scheduleObjRef}
eventSettings={{
dataSource: appointments,
fields: fieldsData,
template: renderEventTemplate,
}}
popupOpen={onPopupOpen}
created={onCreated}
group={{ resources: ['Clients'] }}
resourceHeaderTemplate={data => <ResourceHeaderTemplate data={data} />}
>
<ResourcesDirective>
<ResourceDirective field="ClientId" idField="id" name="Clients" allowMultiple={true} dataSource={clients} />
</ResourcesDirective>
<ViewsDirective>
<ViewDirective displayName="Day" option="TimelineDay" isSelected />
<ViewDirective displayName="Week" option="TimelineWeek" />
<ViewDirective displayName="Month" option="TimelineMonth" />
</ViewsDirective>
<Inject services={[TimelineViews, TimelineMonth]} />
</ScheduleComponent>
</>
);
}





MG Mugilraj Govindarajan Syncfusion Team July 5, 2023 05:13 PM UTC

Hi Jessica,

We checked your requirement based on your shared details at our end and we suggest you pass the popupOpen event args.target(Contains clicked cell or appointment element) element in setAnchorEl, this will open popper component in respective target cell or appointment. Kindly check the below sample and let us know if you need further assistance.

Sample: https://stackblitz.com/edit/react-fcu7ov?file=demo.tsx,index.html

[demo.tsx]

function onPopupOpen(args) {

  if (args.type === 'QuickInfo') {

      args.cancel = true;

      setPopupOpen(true);

       setAnchorEl(args.target);

    }

  }


Regards,

Mugilraj G



JN Jessica Newman July 7, 2023 02:28 PM UTC

Hi there, 

I figured the issue out. For some reason, I was unable to grab a valid DOM node from the popupOpen method, but I was able to via the cellClick and eventClick methods. Do you know why this might be? I have implemented this fix but would like to understand why this was happening. 


Additionally, I realized that when I navigate to my month view, cellDetails is set to null which means that the correct cell cannot be found. I believe it has to do with the date.  Are you able to assist? This is my code for the eventClick method. 

function handleEventClick(args) {
let resourceIndex = sortedClients.findIndex(c => c.id === args.event.ClientId);
let start = args.event.startTime.getTime();
let end = args.event.endTime.getTime();
let time = (scheduleObjRef.current.timeScale.interval / scheduleObjRef.current.timeScale.slotCount) * 1000 * 60;
for (let i = start; i < end; i += time) {
let cellDetails = scheduleObjRef.current.element.querySelector(
'.e-work-cells[data-date="' + i + '"][data-group-index="' + resourceIndex + '"]',
);
setAnchorEl(cellDetails);
}
}


SK Satheesh Kumar Balasubramanian Syncfusion Team July 10, 2023 11:35 AM UTC

Hi Jessica,

We have checked your reported query and let you know that you can use args.event.target on cellClick and args.originalEvent.target on eventClick to achieve your requirement instead of querying the cell details.


[demo.tsx]:

  function onCellClick(args) {
    args.cancel = true;
    setPopupOpen(true);
    setAnchorEl(args.event.target);
  }

  function onEventClick(args) {
    args.cancel = true;
    setPopupOpen(true);
    setAnchorEl(args.originalEvent.target);
  }

Regards,
Satheesh Kumar B


JN Jessica Newman July 10, 2023 02:21 PM UTC

I am using this function for three different views: timeline day, timeline week, and timeline month. The eventClick function is working for my timeline day and timeline week views, but my issues lies within the timeline month view. As mentioned above, I need to retrieve the td component on event click. This is why I am not simply setting my anchor element to the div, but rather I am implementing the logic in the function to retrieve the td cell. Can you please offer a solution that uses the logic in this function and also works for the timeline month view? 

function handleEventClick(args) {
let resourceIndex = sortedClients.findIndex(c => c.id === args.event.ClientId);
let start = args.event.startTime.getTime();
let end = args.event.endTime.getTime();
let time = (scheduleObjRef.current.timeScale.interval / scheduleObjRef.current.timeScale.slotCount) * 1000 * 60;
for (let i = start; i < end; i += time) {
let cellDetails = scheduleObjRef.current.element.querySelector(
'.e-work-cells[data-date="' + i + '"][data-group-index="' + resourceIndex + '"]',
);
setAnchorEl(cellDetails);
}
}


RV Ravikumar Venkatesan Syncfusion Team July 11, 2023 05:01 PM UTC

Jessica,


Sample:  ej2-react-schedule-with-popper-customization - StackBlitz


You can set the target for the Popper component on the event click in the TimelineMonth view by making the below-highlighted changes in the handleEventClick method.


[demo.tsx]

import { ScheduleComponentViewsDirectiveViewDirectiveResourcesDirectiveResourceDirectiveTimelineViewsTimelineMonthInjectresetTime } from '@syncfusion/ej2-react-schedule';

 

export default function SimplePopper() {

 

  function handleEventClick(args) {

    let resourceIndex = clients.findIndex(c => c.id === args.event.ClientId);

    let start = (scheduleObjRef.current.currentView === 'TimelineMonth' ? resetTime(args.event.startTime) : args.event.startTime).getTime();

    let end = (scheduleObjRef.current.currentView === 'TimelineMonth' ? resetTime(args.event.endTime) : args.event.endTime).getTime();

    let time = scheduleObjRef.current.currentView === 'TimelineMonth' ? 86400000 : (scheduleObjRef.current.timeScale.interval / scheduleObjRef.current.timeScale.slotCount) * 1000 * 60;

    for (let i = starti <= endi += time) {

      let cellDetails = scheduleObjRef.current.element.querySelector('.e-work-cells[data-date="' + i + '"][data-group-index="' + resourceIndex + '"]');

      setAnchorEl(cellDetails);

    }

  }

}



JN Jessica Newman July 11, 2023 08:57 PM UTC

This is tremendously helpful, thank you!



SK Satheesh Kumar Balasubramanian Syncfusion Team July 12, 2023 07:27 AM UTC

Jessica,


You're welcome! Thanks for your feedback.



JN Jessica Newman January 3, 2024 01:53 AM UTC

Hi there - I have had to adjust my code and am encountering the same issue whereby I can retrieve a td element, but when I set it as an anchor element my console is telling me that "Node cannot be found in the current page." This is the node I am grabbing: 

<td aria-selected="false" colspan="1" class="e-work-cells e-alternate-cells e-work-hours" data-date="1704225600000" data-group-index="0"></td>. Additionally, if I have more than one appointment in this view (e.g. multiple clients and each client has multiple appointments) the behavior is not working correctly. Can you please prepare a demo for me to take a look at that works? I have a deadline for this feature, so I would really appreciate it this request could be expedited. 

Also, the customPopper component is using the material UI popper component 

Thank you! 

import { useRef, useState, useEffect } from 'react';
import {
ScheduleComponent,
Day,
Week,
Month,
Inject,
ViewsDirective,
ViewDirective,
ResourcesDirective,
ResourceDirective,
resetTime,
} from '@syncfusion/ej2-react-schedule';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import ResourceHeaderTemplate from './ResourceHeaderTemplate';
import useDashboardFilters, { dashboardFilterPeriods } from '../../../../../hooks/useDashboardFilters';
import { addMinutes, parseISO, differenceInDays, isToday, startOfDay, endOfDay } from 'date-fns';
import EventTemplate from './EventTemplate';
import { Typography } from '@mui/material';
import style from '../../../../../styles/CalendarStyles.css';
import { format24HrTime } from '../../../../../utils/utility';
import CreateNewTreatmentEventModal from '../CreateNewTreatmentEventModal';
import CustomPopper from '../../../../components/CustomPopper';
import QuickInfoContent from './QuickInfoContent';

const fieldsData = {
id: 'id',
startTime: { name: 'startTime' },
endTime: { name: 'endTime' },
subject: { name: 'appointment' },
};

export default function CalendarView({ data, clients }) {
const { period, setPeriod } = useDashboardFilters();
const scheduleObjRef = useRef();
const [currentView, setCurrentView] = useState('');
const [selectedDate, setSelectedDate] = useState('');
const [currentClient, setCurrentClient] = useState(null);
const [appointmentDetails, setAppointmentDetails] = useState(null);
const [anchorEl, setAnchorEl] = useState(null);

useEffect(() => {
const daysDifference = differenceInDays(period.to(), period.from());
if (daysDifference < 0) {
return;
} else if (daysDifference === 0) {
setCurrentView('Day');
} else if (daysDifference > 0 && daysDifference <= 6) {
setCurrentView('Week');
} else if (daysDifference > 6) {
setCurrentView('Month');
}
setSelectedDate(period.from());
}, [period]);

function handleNavigation(args) {
if (args.action === 'view') {
const currentView = args.currentView;
if (currentView === 'Day') {
const currentDate = args.currentDate;
if (isToday(currentDate) && period.label !== 'Custom') {
setPeriod(dashboardFilterPeriods.today);
} else {
setPeriod({ ...dashboardFilterPeriods.custom, from: () => startOfDay(currentDate), to: () => endOfDay(currentDate) });
}
}
}
}

function renderEventTemplate(event) {
return <EventTemplate event={event} />;
}

function onEventClick(args) {
args.cancel = true;
const startTime = currentView === 'Month' ? resetTime(args.event.startTime).getTime() : args.event.startTime.getTime();
const resourceIndex = clients.findIndex(p => p.id === args.event.clientId);
const cellDetails = scheduleObjRef.current.element.querySelector(
'.e-work-cells[data-date="' + startTime + '"][data-group-index="' + resourceIndex + '"]',
);
setAnchorEl(cellDetails);
setAppointmentDetails(args.event);
setCurrentClient(clients.find(p => p.id === args.event.clientId));
}

function handleClosePopper() {
setAnchorEl(null);
setCurrentClient(null);
setAppointmentDetails(null);
}

return (
<>
{anchorEl && (
<CustomPopper
title="View Event"
arrow={false}
open={!!anchorEl}
onClose={handleClosePopper}
client={currentClient}
Content={<QuickInfoContent client={currentClient} appointmentDetails={appointmentDetails} onClose={handleClosePopper} />}
/>
)}
<SimpleBar style={{ maxHeight: '100%', overflowX: 'hidden' }}>
<ScheduleComponent
classes={style}
ref={scheduleObjRef}
eventSettings={{ fields: fieldsData, dataSource: data, template: renderEventTemplate }}
showHeaderBar={false}
selectedDate={selectedDate || new Date()}
currentView={currentView}
timeScale={{ enable: true, interval: 30, slotCount: 2 }}
group={{ resources: ['Clients'] }}
resourceHeaderTemplate={data => <ResourceHeaderTemplate data={data} />}
navigating={handleNavigation}
startHour={format24HrTime(period.from())}
endHour={format24HrTime(period.to())}
eventClick={onEventClick}
>
<ResourcesDirective>
<ResourceDirective field="clientId" idField="id" name="Clients" dataSource={clients} />
</ResourcesDirective>
<Inject services={[Day, Week, Month]} />
<ViewsDirective>
<ViewDirective option="Day" />
<ViewDirective option="Week" />
<ViewDirective option="Month" />
</ViewsDirective>
</ScheduleComponent>
</SimpleBar>
</>
);
}



RR Ram Raju Elaiyaperumal Syncfusion Team January 3, 2024 02:43 PM UTC

Hi Jessica Newman,

We have attempted to reproduce your reported issue based on the code snippet you have provided, but unfortunately, we were unable to replicate the problem on our end. To assist you more effectively, we would appreciate if you could provide additional details. Specifically, it would be helpful if you could reproduce the issue in the sample we shared or provide a sample that reproduces the issue.


By doing so, we can better understand the context and conditions under which the issue occurs, which will enable us to provide a more accurate and effective solution. Thank you for your cooperation.


Regards,

Ram


Attachment: popup_1_7d803847.zip

Loader.
Up arrow icon