We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date
Syncfusion site will be temporarily unavailable for scheduled maintenance on December 14, 2024, from 10:30 PM ET to 11:30 PM ET.
close icon

CRUD Action

Hi there,

I have a custom EditorTemplate and custom Popup Footer that I am trying to get working and I am slightly confused about the use of addEvent, saveEvent and deleteEvent. How do I ensure that my state is updated upon changing an input? Or deleting an event? Sometimes it seems as though my data persists, and other times it doesn't. The below code works for a saveEvent, but my add and delete events in my custom Popup Footer does not. Also, do I need to manually update my state as I am doing below? 

If you do send an example via StackBlitz, please create it using React Functional Components.

Save Event

function handleChange(e) {
const { name, value } = e.target;
const newEvent = {
...event,
[name]: value,
};
scheduleObjRef.current.saveEvent(newEvent);
}


Add and Delete in Custom Popup Footer

function buttonClickActions(e) {
let appDetail = getQuickPopupData();
const getSlotData = () => {
const { startTime, endTime } = currScheduleObjRef.current.activeCellsData;
const newEvent = {};
newEvent.Id = currScheduleObjRef.current.getEventMaxID();
newEvent.EndTime = endTime;
newEvent.StartTime = startTime;
newEvent.ResourceId = Resource.id;
newEvent.Subject = appDetail.Prescription;
newEvent.Status = determineStatus(startTime, endTime);
return newEvent;
};

if (e.target.id === "add") {
//I don't think I need to set state here - addEvent should add to dataSource automatically
const newEvent = getSlotData();
setData((prev) =>
prev.map((p) =>
p.id === newEvent.ResourceId
? { ...p, appointments: [...p.appointments, newEvent] }
: p
)
);
currScheduleObjRef.current.addEvent(newEvent);
} else if (e.target.id === "delete") {
const eventToDelete = currScheduleObjRef.current.activeEventData.event;
setData((prev) =>
prev.map((p) =>
p.id === eventToDelete.ResourceId
? {
...p,
appointments: p.appointments.filter(
(appointment) => appointment.Id !== eventToDelete.Id
),
}
: p
)
);
currScheduleObjRef.current.deleteEvent(eventToDelete, "Delete");
} else {
const isCellPopup = appointment.elementType === "cell";
const eventDetails = isCellPopup
? getSlotData()
: currScheduleObjRef.current.activeEventData.event;
let currentAction = isCellPopup ? "Add" : "Save";
currScheduleObjRef.current.openEditor(eventDetails, currentAction, true);
}
currScheduleObjRef.current.quickPopup.quickPopupClose();
}


Thank you.


7 Replies

JN Jessica Newman March 16, 2023 05:32 PM UTC

Hi - Are you able to provide an update/solution?



JN Jessica Newman March 16, 2023 08:57 PM UTC

Additionally, are you able to provide a custom Editor Template sample using React Functional Components? I am having a difficult time figuring out how to update inputs in my custom Editor Template and have the changes persist to my local data. Please advise. 


Thank you.



RV Ravikumar Venkatesan Syncfusion Team March 17, 2023 03:17 PM UTC

Hi Jessica,


Q1: How do I ensure that my state is updated upon changing an input? Or deleting an event? Sometimes it seems as though my data persists, and other times it doesn't. The below code works for a saveEvent, but my add and delete events in my custom Popup Footer does not. Also, do I need to manually update my state as I am doing below?


Demo: https://ej2.syncfusion.com/react/demos/#/material/schedule/quick-info-template

UG: https://ej2.syncfusion.com/react/documentation/schedule/editor-template#customizing-quick-popups


You can perform CRUD actions with the Schedule using the quickInfoTemplates and editorTemplate with the state as shown in the below code snippet with help of the actionBegin event of the Schedule.


[App.js]

function App() {

  let scheduleObj;

  let dateRange = {};

  let quickPopupData = {};

  const [scheduleModel, setScheduleModel] = useState({

    currentAction: "InitialRendering",

    currentView: "Week",

    selectedDate: new Date(2021, 1, 15),

    dataSource: getCurrentViewApps(new Date(2021, 1, 15), "Week", true)

  });

 

  function headerTemplate(props) {

    return <HeaderTemplate data={props}></HeaderTemplate>

  }

 

  function contentTemplate(props) {

    return <ContentTemplate data={props} handleChange={handleTextChange}></ContentTemplate>

  }

 

  function footerTemplate(props) {

    return <FooterTemplate data={props} getQuickData={getQuickPopupData} scheduleObj={scheduleObj}></FooterTemplate>;

  }

 

  function handleTextChange(quickData) {

    quickPopupData = quickData;

  }

 

  function getQuickPopupData() {

    return quickPopupData;

  }

 

  function editorTemplate(props) {    

    return <EditorTemplate data={props}></EditorTemplate>;    

  }

 

  function onActionBegin(args) {

    if (["eventCreate", "eventChange", "eventRemove"].indexOf(args.requestType) > -1) {

      // Prevent the default CRUD action by setting up true

      args.cancel = true;

      let resultData = extend([], scheduleModel.dataSource, null, true);

      if (args.addedRecords.length > 0) {

        // Adding appointment

        resultData = resultData.concat(args.addedRecords);

      }

      if (args.changedRecords.length > 0) {

        for (let i = 0; i < args.changedRecords.length; i++) {

          const index = resultData.findIndex((data) => { return data.Id === args.changedRecords[i].Id });

          // Updating appointment

          resultData[index] = args.changedRecords[i];

        }

      }

      if (args.deletedRecords.length > 0) {

        for (let i = 0; i < args.deletedRecords.length; i++) {

          const index = resultData.findIndex((data) => { return data.Id === args.deletedRecords[i].Id });

          // Deleting appointment

          resultData.splice(index, 1);

        }

      }

      // Update the state with the new data

      setScheduleModel(prevState => ({ ...prevState, dataSource: resultData }));

    }

  }

 

  return (

    <div className="App">

      <ScheduleComponent width='100%' height='650px' ref={t => scheduleObj = t} selectedDate={scheduleModel.selectedDate} currentView={scheduleModel.currentView}

        eventSettings={{ dataSource: scheduleModel.dataSource }} quickInfoTemplates={{

          header: headerTemplate.bind(this), content: contentTemplate.bind(this),

          footer: footerTemplate.bind(this)

        }} editorTemplate={editorTemplate.bind(this)} actionBegin={onActionBegin.bind(this)} navigating={onNavigating.bind(this)}>

      </ScheduleComponent>

    </div>

  );

}

 

export default App;


When a new appointment is added through the addEvent method and an appointment deleted event deleteEvent method as shown in the below code snippet. You can update the state in the actionBegin event of the Schedule as shown in the above code snippet. For editorTemplate while clicking the Save, or Delete button the actionBegin method is automatically triggered or you can use the addEvent, saveEvent, and deleteEvent methods of the Schedule. So, you can update the state as in the above code snippet.


[footer-template.js]

function FooterTemplate(props) {

 

  function buttonClickActions(e) {

    var appDetail = props.getQuickData();

    var scheduleObj = props.scheduleObj;

    const getSlotData = () => {

      const addObj = {};

      addObj.Id = scheduleObj.getEventMaxID();

      addObj.Subject = appDetail && appDetail.Subject ? appDetail.Subject : 'Add title';

      addObj.StartTime = new Date(scheduleObj.activeCellsData.startTime);

      addObj.EndTime = new Date(scheduleObj.activeCellsData.endTime);

      addObj.IsAllDay = scheduleObj.activeCellsData.isAllDay;

      addObj.Description = appDetail && appDetail.Description ? appDetail.Description : 'Add notes';

      return addObj;

    };

    if (e.target.id === 'add') {

      const addObj = getSlotData();

      props.scheduleObj.addEvent(addObj);

    }

    else if (e.target.id === 'delete') {

      const eventDetails = scheduleObj.activeEventData.event;

      let currentAction = 'Delete';

      if (eventDetails.RecurrenceRule) {

        currentAction = 'DeleteOccurrence';

      }

      scheduleObj.deleteEvent(eventDetails, currentAction);

    }

    else {

      const isCellPopup = props.data.elementType === "cell";

      const eventDetails = isCellPopup ? getSlotData() : scheduleObj.activeEventData.event;

      let currentAction = isCellPopup ? 'Add' : 'Save';

      if (eventDetails.RecurrenceRule) {

        currentAction = 'EditOccurrence';

      }

      scheduleObj.openEditor(eventDetails, currentAction, true);

    }

    scheduleObj.closeQuickInfoPopup();

  };

 

  return (

    <div className="quick-info-footer">

      {props.data.elementType === "cell" ?

        <div className="cell-footer">

          <ButtonComponent id="more-details" cssClass='e-flat' content="More Details" onClick={buttonClickActions} />

          <ButtonComponent id="add" cssClass='e-flat' content="Add" isPrimary={true} onClick={buttonClickActions} />

        </div>

        :

        <div className="event-footer">

          <ButtonComponent id="delete" cssClass='e-flat' content="Delete" onClick={buttonClickActions} />

          <ButtonComponent id="more-details" cssClass='e-flat' content="More Details" isPrimary={true} onClick={buttonClickActions} />

        </div>}

    </div>

  );

};

export default FooterTemplate;


Q2: a custom Editor Template sample using React Functional Components?


Demo: https://ej2.syncfusion.com/react/demos/#/material/schedule/editor-template

UG: https://ej2.syncfusion.com/react/documentation/schedule/editor-template#customizing-event-editor-using-template


You can use the custom editorTemplate with the Schedule as shown in the below code snippet. If you want the Schedule automatically recognize all the input fields and get the value from each inputs fields and form an appointment object while saving an appointment you need to add the “e-field” class and “name” attributes to each input element.


[App.js]

import EditorTemplate from './editor-template';

 

function App() {

  function editorTemplate(props) {    

    return <EditorTemplate data={props}></EditorTemplate>;    

  }

 

  return (

    <div className="App">

      <ScheduleComponent editorTemplate={editorTemplate.bind(this)}>

      </ScheduleComponent>

    </div>

  );

}

 

export default App;


[editor-template.js]

import React, { useState } from 'react';

import { DateTimePickerComponent } from '@syncfusion/ej2-react-calendars';

import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';

 

function EditorTemplate(props) {

    let data = props?.data;

    const [subject, setSubject] = useState(data?.Subject);

    const [description, setDescription] = useState(data?.Description);

 

    return (

        (data !== undefined) ?

            <table className="custom-event-editor" style={{ width: '100%' }} cellPadding={5}>

                <tbody>

                    <tr>

                        <td className="e-textlabel">Summary</td>

                        <td colSpan={4}>

                            <input id="Summary" className="e-field e-input" type="text" data-name="Subject" value={subject} style={{ width: '100%' }} onChange={(args) => { setSubject(args.target.value) }} />

                        </td>

                    </tr>

                    <tr>

                        <td className="e-textlabel">Status</td><td colSpan={4}>

                            <DropDownListComponent id="EventType" placeholder='Choose status' data-name='EventType' value={data.EventType} className="e-field" style={{ width: '100%' }} dataSource={['New', 'Requested', 'Confirmed']}>

                            </DropDownListComponent>

                        </td>

                    </tr>

                    <tr>

                        <td className="e-textlabel">From</td>

                        <td colSpan={4}>

                            <DateTimePickerComponent id="StartTime" format='dd/MM/yy hh:mm a' data-name="StartTime" value={new Date(data.startTime || data.StartTime)} className="e-field"></DateTimePickerComponent>

                        </td>

                    </tr>

                    <tr>

                        <td className="e-textlabel">To</td>

                        <td colSpan={4}>

                            <DateTimePickerComponent id="EndTime" format='dd/MM/yy hh:mm a' data-name="EndTime" value={new Date(data.endTime || data.EndTime)} className="e-field"></DateTimePickerComponent>

                        </td>

                    </tr>

                    <tr>

                        <td className="e-textlabel">Reason</td>

                        <td colSpan={4}>

                            <textarea id="Description" className="e-field e-input" name="Description" value={description} onChange={(args) => { setDescription(args.target.value) }}></textarea>

                        </td>

                    </tr>

                </tbody>

            </table>

            :

            <div></div>

    );

};

 

export default EditorTemplate;


Regards,

Ravikumar Venkatesan


Attachment: ej2reactschedulequickinfoandeditortemplatesample_33d0b5ab.zip


JN Jessica Newman March 21, 2023 02:07 AM UTC

Thank you! This is tremendously helpful. One more question.

  1. I have a custom Editor Template. Upon saving changes I make, the view of my scheduler immediately jumps back to 9am. So, if it is 6pm I then have to manually scroll to the current time. The behavior I would like my scheduler to exhibit is that after I save an event, for example, I would like the view of the scheduler to remain at the current time. Or, the view to be on the event that I most recently changed. This is what my schedule component looks like.

<ScheduleComponent
width="100%"
height="100vh"
classes={style}
ref={currScheduleObjRef}
selectedDate={new Date()}
eventSettings={{
dataSource: appointments,
template: renderEventTemplate,
}}
group={{ resources: ["Resources"] }}
editorTemplate={(appointment) => (
<EditorTemplate
appointment={appointment}
clients={clients}
currScheduleObjRef={currScheduleObjRef}
sortedInteractions={sortedInteractions}
/>
)}
resourceHeaderTemplate={(data) => (
<ResourceHeaderTemplate data={data} />
)}
quickInfoTemplates={{
header: header,
content: content,
footer: footer,
}}
cellClick={cellClick}
created={onCreated}
actionBegin={onActionBegin}
>
<ResourcesDirective>
<ResourceDirective
field="ResourceId"
title="Client"
name="Resources"
allowMultiple={true}
textField="text"
idField="id"
colorField="color"
dataSource={clients}
/>
ResourcesDirective>
<ViewsDirective>
<ViewDirective
displayName="Day"
option="TimelineDay"
isSelected
/>
<ViewDirective displayName="Week" option="TimelineWeek" />
<ViewDirective displayName="Month" option="TimelineMonth" />
ViewsDirective>
<Inject
services={[TimelineMonth, TimelineViews, Resize, DragAndDrop]}
/>
ScheduleComponent>


RV Ravikumar Venkatesan Syncfusion Team March 21, 2023 06:18 PM UTC

Jessica,


We checked your shared codes at our end. We suspect that the Schedule is fully rerendered or refreshed while the state update is performed on the actionBegin event of the Schedule or any of the sample end customizations causing your reported behavior. So, if you still facing the same behavior share the below details to proceed further.


  • Entire Schedule related code snippets or
  • Reproduce your reported behavior in our shared sample or
  • Sample reproducing your reported behavior.
  • Video showing your reported behavior.


JN Jessica Newman replied to Ravikumar Venkatesan March 24, 2023 10:07 PM UTC

Hi, 

Yes, that is correct. Upon an update occurring in the custom editor template or the custom quick pop up, the Schedule component is rerendered and the current display time always resets to 9am. I am not comfortable sharing my entire codebase in a public forum, but do not know how to replicate the issue on your end since I do not know what causes it. If possible, are you able to provide a few reasons that could cause the component to re-render this way? I have copied the code you provided above almost verbatim in my application, so not much is different.


Additionally, is it possible to use Material UI components in tandem with Syncfusion schedule? I originally was using Material UI inputs on my editor template, but could not get the schedule functionality to work which is why I changed to using Syncfusion components. 


Thanks in advance. 



RV Ravikumar Venkatesan Syncfusion Team March 27, 2023 02:31 PM UTC

Hi Jessica,


We have created a new ticket under your account to follow up on your query. We suggest you follow up with the ticket for further updates. Please log in using the below link.


https://support.syncfusion.com/support


Regards,

Ravikumar Venkatesan


Loader.
Up arrow icon