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

Binding issues

First of all thank you for this great tool. Probably my fault, I have not been able to use it efficiently yet. Following is my case:

I use Axios to fetch data from the api like the following code, payload determines the security and the query:

setData({ result: [], count: 0 })
    setLoading(true)

    const payload = {
      token: localStorage.getItem("PM_TOKEN"),
      docusignAccessToken: localStorage.getItem("PM_DOCUSIGNACCESSTOKEN"),
      adminId: localStorage.getItem("PM_ADMINID"),
      fields: {
        id: true,
        website: true,
        property: true,
        checkIn: true,
        checkOut: true,
        guestName: true,
        guestEmail: true,
        guestPhone: true,
        guestProblem: true,
        status: true,
        docusignId: true,
        docusignStatus: true,
        docusignButton: true,
      },
      filters: {
        propertyIdList: propertyIdList,
        guestName: guest === "" ? null : guest,
        checkInMin:
          dateFrom === null ? null : Moment(dateFrom).format("yyyy-MM-DD"),
        checkInMax:
          dateTo === null ? null : Moment(dateTo).format("yyyy-MM-DD"),
      },
    }

    axios
      .post(process.env.REACT_APP_API_BASE_URL + "reservation/list", payload)
      .then(response => {
        setData({ result: response.data, count: response.data.length })
        setLoading(false)
      })
      .catch(() => {
        setLoading(false)
      })

I'll also post the whole code but I have some various issues with the Grid:

User clicks on the Search button. Search button makes the api call according to filters on the page, and a similar snapshot looks like this is displayed:

0.JPG

At this point I've some issues:

1- Later the grid filled out if any state change happens the template columns (Arrival,Departure,Docusign Status) loose their content:

1.JPG

2- If I remove the following condition the grid never displayed and the page crashes for after timeout.

{data.count > 0 && (


3- The grid at the top image can be exported to pdf however if there are more rows (about 50-60) PDF Export not working and I see a log in console like the following:

2.JPG

Here are my full codes:

import React, { useReducer, useState } from "react"
import {
  IonButton,
  IonItem,
  IonLabel,
  IonProgressBar,
  IonSpinner,
  IonSelect,
  IonSelectOption,
  IonInput,
  IonToast,
  IonModal,
  IonGrid,
  IonRow,
  IonCol,
  IonContent,
  IonIcon,
} from "@ionic/react"
import axios from "axios"
import Moment from "moment"
import { IProperty, IToast } from "../lib/sharedInterface"
import DatePicker from "../components/DatePicker"
import Reservation from "../components/Reservation"
import {
  ColumnDirective,
  ColumnsDirective,
  GridComponent,
  Inject,
  Sort,
  Filter,
  PdfExport,
  ExcelExport,
  Toolbar,
  Resize,
} from "@syncfusion/ej2-react-grids"
import { App } from "@capacitor/app"
import { Capacitor } from "@capacitor/core"
import { download } from "ionicons/icons"

interface IState {
  toast: IToast | null
  showToast: boolean
}

const Reservations: React.FC = () => {
  const [data, setData] = useState<any>({ result: [], count: 0 })
  const [loading, setLoading] = useState<boolean>(true)
  const [propertyIdList, setPropertyIdList] = useState<number[] | null>(null)
  const [propertyList, setPropertyList] = useState<IProperty[]>([])
  const [guest, setGuest] = useState("")
  const [dateFrom, setDateFrom] = useState<string | null>(
    Moment(new Date()).format("yyyy-MM-DD")
  )
  const [dateTo, setDateTo] = useState<string | null>(null)
  const [state, setState] = useReducer(
    (state: IState, newState: Partial<IState>) => ({
      ...state,
      ...newState,
    }),
    {
      toast: null,
      showToast: false,
    }
  )
  const [reservationId, setReservationId] = useState<number | null>(null)
  const ios = Capacitor.getPlatform() === "ios"
  let grid: GridComponent | null
  const toolbar = ["PdfExport", "ExcelExport"]
  const toolbarClick = (args: { item: { id: string } }) => {
    if (grid) {
      if (args.item.id === "grid_pdfexport") {
        grid.pdfExport()
      } else if (args.item.id === "grid_excelexport") {
        grid.excelExport()
      }
    }
  }

  const template = (args: any) => {
    switch (args.column.field) {
      case "checkIn":
        return <div>{Moment(args.checkIn).format("L")}</div>
      case "checkOut":
        return <div>{Moment(args.checkOut).format("L")}</div>
      case "docusignStatus":
        return (
          <div>
            {args.docusignStatus}
            {args.docusignStatus !== "" && (
              <IonIcon
                icon={download}
                slot="end"
                onClick={() => DownloadDocusignDocument(args.docusignId ?? "")}
                size="large"
              />
            )}
            {args.docusignButton && (
              <IonButton onClick={() => SendDocusignDocument(args)}>
                Send
              </IonButton>
            )}
          </div>
        )
    }
  }

  const dataBound = () => {
    if (grid) {
      grid.autoFitColumns([
        "checkIn",
        "checkOut",
        "property",
        "guestName",
        "guestEmail",
        "guestPhone",
        "website",
        "status",
      ])
    }
  }

  const handleRefresh = () => {
    if (grid) {
      grid.refresh()
    }
  }

  React.useEffect(() => {
    const payload = {
      token: localStorage.getItem("PM_TOKEN"),
      adminId: localStorage.getItem("PM_ADMINID"),
      fields: {
        id: true,
        property: true,
      },
    }

    axios
      .post(process.env.REACT_APP_API_BASE_URL + "property/list", payload)
      .then(response => {
        let list = response.data.map((p: IProperty) => ({
          id: p.id,
          property: p.property,
        }))
        setPropertyList(list)

        let listId: number[] = []
        response.data.map((p: IProperty) => (listId = listId.concat(p.id ?? 0)))
        setPropertyIdList(listId)
        setLoading(false)
      })
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const DownloadDocusignDocument = (docusignId: string) => {
    setLoading(true)

    const payload = {
      token: localStorage.getItem("PM_TOKEN"),
      docusignAccessToken: localStorage.getItem("PM_DOCUSIGNACCESSTOKEN"),
      adminId: localStorage.getItem("PM_ADMINID"),
      docusignId: docusignId,
    }

    axios
      .post(
        process.env.REACT_APP_API_BASE_URL +
          "reservation/downloadDocusignDocument",
        payload,
        { responseType: "blob" }
      )
      .then(response => {
        const file = new Blob([response.data], { type: "application/pdf" }) //Build a URL from the file
        const fileURL = URL.createObjectURL(file) //Open the URL on new Window
        window.open(fileURL)

        setLoading(false)
      })
      .catch(() => {
        setLoading(false)
      })
  }

  const SendDocusignDocument = (args: any) => {
    setLoading(true)

    const payload = {
      token: localStorage.getItem("PM_TOKEN"),
      docusignAccessToken: localStorage.getItem("PM_DOCUSIGNACCESSTOKEN"),
      adminId: localStorage.getItem("PM_ADMINID"),
      reservationId: args.id,
    }

    axios
      .post(
        process.env.REACT_APP_API_BASE_URL + "reservation/sendDocusignDocument",
        payload
      )
      .then(response => {
        if (response.data.response) {
          args.docusignButton = false
          args.docusignStatus = "sent"
        }

        setState({
          toast: {
            explanationType: response.data.explanationType,
            explanation: response.data.explanation,
          },
          showToast: true,
        })

        setLoading(false)
      })
      .catch(() => {
        setState({
          toast: {
            explanationType: "danger",
            explanation: "Error occured",
          },
          showToast: true,
        })

        setLoading(false)
      })
  }

  const Search = () => {
    setData({ result: [], count: 0 })
    setLoading(true)

    const payload = {
      token: localStorage.getItem("PM_TOKEN"),
      docusignAccessToken: localStorage.getItem("PM_DOCUSIGNACCESSTOKEN"),
      adminId: localStorage.getItem("PM_ADMINID"),
      fields: {
        id: true,
        website: true,
        property: true,
        checkIn: true,
        checkOut: true,
        guestName: true,
        guestEmail: true,
        guestPhone: true,
        guestProblem: true,
        status: true,
        docusignId: true,
        docusignStatus: true,
        docusignButton: true,
      },
      filters: {
        propertyIdList: propertyIdList,
        guestName: guest === "" ? null : guest,
        checkInMin:
          dateFrom === null ? null : Moment(dateFrom).format("yyyy-MM-DD"),
        checkInMax:
          dateTo === null ? null : Moment(dateTo).format("yyyy-MM-DD"),
      },
    }

    axios
      .post(process.env.REACT_APP_API_BASE_URL + "reservation/list", payload)
      .then(response => {
        setData({ result: response.data, count: response.data.length })
        setLoading(false)
      })
      .catch(() => {
        setLoading(false)
      })
  }

  React.useEffect(() => {
    if (reservationId === null && !loading) {
      Search()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [reservationId])

  React.useEffect(() => {
    App.addListener("backButton", () => {
      setReservationId(null)
    })

    return () => {
      App.removeAllListeners()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [ios])

  return (
    <IonContent>
      <IonGrid>
        <IonRow>
          <IonCol sizeXs="12" sizeMd="8">
            <IonLabel>Properties</IonLabel>
            {propertyIdList === null && <IonSpinner name="bubbles" />}
            {propertyIdList !== null && (
              <IonSelect
                value={propertyIdList ?? []}
                multiple={true}
                cancelText="Cancel"
                okText="Done"
                onIonChange={e => setPropertyIdList(e.detail.value)}
              >
                {propertyList.map((p, index) => {
                  return (
                    <IonSelectOption key={p.id} value={p.id}>
                      {p.property}
                    </IonSelectOption>
                  )
                })}
              </IonSelect>
            )}
          </IonCol>
          <IonCol sizeXs="12" sizeMd="4">
            <IonInput
              value={guest}
              placeholder="Guest"
              onIonChange={e => setGuest(e.detail.value!)}
              clearInput
            />
          </IonCol>
        </IonRow>
        <IonRow>
          <IonCol sizeXs="12" sizeSm="6" sizeMd="4">
            <DatePicker
              id="dpReservations1"
              label="Date From"
              dateValue={dateFrom}
              setDateValue={(val: string | null) =>
                setDateFrom(
                  val === null ? null : Moment(val).format("MM/DD/yyyy")
                )
              }
              presentation="date"
              side="bottom"
            />
          </IonCol>
          <IonCol sizeXs="12" sizeSm="6" sizeMd="4">
            <DatePicker
              id="dpReservations2"
              label="Date To"
              dateValue={dateTo}
              setDateValue={(val: string | null) =>
                setDateTo(
                  val === null ? null : Moment(val).format("MM/DD/yyyy")
                )
              }
              presentation="date"
              side="bottom"
            />
          </IonCol>
          <IonCol sizeXs="12" sizeMd="4">
            <IonButton
              onClick={() => Search()}
              expand="full"
              disabled={loading}
            >
              Search
            </IonButton>
          </IonCol>
        </IonRow>
      </IonGrid>

      {data.count > 0 && (
        <IonItem class="ion-no-padding">
          <GridComponent
            id="grid"
            dataSource={data}
            ref={g => (grid = g)}
            toolbar={toolbar}
            allowPdfExport={true}
            allowExcelExport={true}
            toolbarClick={toolbarClick}
            allowFiltering={false}
            allowSorting={true}
            loadingIndicator={{ indicatorType: "Shimmer" }}
            dataBound={dataBound}
            dataStateChange={handleRefresh}
          >
            <Inject
              services={[Toolbar, PdfExport, ExcelExport, Sort, Filter, Resize]}
            />
            <ColumnsDirective>
              <ColumnDirective
                key={0}
                field="checkIn"
                headerText="Arrival"
                width={100}
                template={template}
              />
              <ColumnDirective
                key={1}
                field="checkOut"
                headerText="Departure"
                width={100}
                template={template}
              />
              <ColumnDirective
                key={2}
                field="property"
                headerText="Property"
                width={100}
              />
              <ColumnDirective
                key={3}
                field="guestName"
                headerText="Guest"
                width={100}
              />
              <ColumnDirective
                key={4}
                field="guestEmail"
                headerText="Email"
                width={100}
              />
              <ColumnDirective
                key={5}
                field="guestPhone"
                headerText="Phone"
                width={100}
              />
              <ColumnDirective
                key={6}
                field="website"
                headerText="Channel"
                width={100}
              />
              <ColumnDirective
                key={7}
                field="status"
                headerText="Status"
                width={100}
              />
              <ColumnDirective
                key={8}
                field="docusignStatus"
                headerText="Docusign Status"
                width={150}
                textAlign="Center"
                template={template}
              />
            </ColumnsDirective>
          </GridComponent>
        </IonItem>
      )}

      {loading && <IonProgressBar type="indeterminate"></IonProgressBar>}

      <IonModal
        isOpen={reservationId !== null}
        onDidDismiss={() => setReservationId(null)}
      >
        <Reservation id={reservationId} close={() => setReservationId(null)} />
      </IonModal>

      <IonToast
        isOpen={state.showToast}
        onDidDismiss={() => setState({ showToast: false })}
        message={state.toast?.explanation}
        duration={2000}
        color={state.toast?.explanationType}
      />
    </IonContent>
  )
}

export default Reservations

Please advise, thank you.


1 Reply

RR Rajapandi Ravi Syncfusion Team January 13, 2023 11:37 AM UTC

Emrah,


Greetings from Syncfusion support


Query#1)


From your query we could see that you are having a template for  Arrival,Departure,Docusign Status columns and losing their content if any state change occurs. While changing state variable due to refresh the templates are getting empty. To prevent the refresh for template columns, we need to use statelessTemplates while updating state value with template feature enabled in grid component. Also, we would like to inform you that, we provided the support for this since Vol 4 release. So please update your Syncfusion packages to the latest version and resolve the problem.


 

<GridComponent id="grid" dataSource={data} statelessTemplates={['directiveTemplates']} height={150}>

        <Inject services={[Toolbar, ExcelExport]} />

        <ColumnsDirective>

          <ColumnDirective field="OrderID" width="100" textAlign="Right" />

          <ColumnDirective field="ShipCity" width="100" />

          <ColumnDirective field="EmployeeID" width="100" textAlign="Right" />

          <ColumnDirective

            field="Freight"

            width="100"

            format="C2"

            textAlign="Right"

          />

          <ColumnDirective field="ShipName" width="100" template={template} />

        </ColumnsDirective>

      </GridComponent>

 


Sample: https://stackblitz.com/edit/react-1xewxm-gvvm18?file=index.js


Query#2)


Now you are using the condition with data.count > 0 and you are facing the problem once you remove the condition, we suspect that at the execution time if there is no data it will affect the template rendering and it will cause the crash. So please debug your application at your end and to prevent the crash use the respective conditions.


Query#3)


Based on your shared information we have prepared a sample and tried to reproduce your reported problem at our end, but it was unsuccessful.


If you still face the issue, please try to reproduce the issue with our above attached sample and share the video demonstration that will be helpful for us to validate the exact issue scenario.


Regards,

Rajapandi R


Loader.
Up arrow icon