BoldSignA modern eSignature application with affordable pricing. Sign up today for unlimited document usage!
Hi There,
We are trying to set up Custom Sort based on Server Side call but not able to reflect this sorting on Client side
Our code is follows:
import { JATOToastContainer } from '@jato/ui-component-library'
import {
ActionEventArgs,
ColumnDirective,
ColumnsDirective,
Edit,
EditSettingsModel,
Grid,
GridComponent,
Inject,
Page,
PageSettingsModel,
RowDataBoundEventArgs,
RowDeselectEventArgs,
RowSelectEventArgs,
Selection,
SelectionSettingsModel,
Sort,
SortSettingsModel,
} from '@syncfusion/ej2-react-grids'
import { vehicleGridPageSize, vehicleSelectionLimit } from 'config'
import { selectedFilters } from 'features/filters/selectedFiltersSlice'
import {
addMpVehicle,
mpVehicles,
removeMpVehicle,
} from 'features/mpvehicles/mpVehiclesSlice'
import { FilterNameType } from 'models/Filters/FilterNameType'
import { ISelectedFilterOption } from 'models/Filters/FilterOption'
import { MpVehicle } from 'models/VehicleSelection/MpVehicle'
import { MpVehiclesResponse } from 'models/VehicleSelection/MpVehiclesResponse'
import React, { useEffect } from 'react'
import { useAppDispatch, useAppSelector } from 'store/hook'
import { StyledVehicleGrid } from './VehicleGrid.styles'
export interface IVehicleGridProps {
MpVehiclesResponse: MpVehiclesResponse
onPageChangeEvent: (ev: ActionEventArgs) => void
onSortChangeEvent: (ev: ActionEventArgs) => void
}
export const VehicleGrid: React.FC<IVehicleGridProps> = (
props: IVehicleGridProps
) => {
let gridInstance: Grid | null
const dispatch = useAppDispatch()
const mpVehicleData: MpVehicle[] = useAppSelector(mpVehicles)
const selectedFiltersData: ISelectedFilterOption[] = useAppSelector(
selectedFilters
)
const gridData = props.MpVehiclesResponse.vehicles
const pageSettings: PageSettingsModel = {
pageSize: vehicleGridPageSize,
currentPage: 1,
totalRecordsCount: props.MpVehiclesResponse.count,
}
const sortingOptions: SortSettingsModel = {
columns: [{ field: 'make', direction: 'Ascending' }],
}
const editOptions: EditSettingsModel = {
allowEditing: false,
allowAdding: false,
allowDeleting: false,
mode: 'Normal',
}
const selectionSettings: SelectionSettingsModel = {
checkboxMode: 'Default',
checkboxOnly: true,
persistSelection: true,
}
const rowSelected = (row: RowSelectEventArgs): void => {
if (row.isInteracted) {
const mpVehicleData = row.data as MpVehicle
if (row.rowIndexes?.length === vehicleSelectionLimit) {
if (gridInstance) gridInstance.refresh()
}
dispatch(addMpVehicle(mpVehicleData))
}
}
const rowDeselected = (row: RowDeselectEventArgs): void => {
if (row.isInteracted) {
if (gridInstance) {
const selectedRows = gridInstance.getSelectedRows().length
if (selectedRows === vehicleSelectionLimit - 1) gridInstance.refresh()
}
dispatch(removeMpVehicle(row.data as MpVehicle))
}
}
const actionBegin = (args: ActionEventArgs): void => {
if (args.requestType === 'paging') {
args.cancel = true
props.onPageChangeEvent(args)
} else if (args.requestType === 'sorting') {
// args.cancel = true
props.onSortChangeEvent(args)
}
}
const dataBound = (): void => {
if (gridInstance) {
gridInstance.pageSettings.currentPage = 1
gridInstance.pageSettings.pageSize = vehicleGridPageSize
if (selectedFiltersData.length > 0) {
for (let i = 0; i < selectedFiltersData.length; i++) {
if (selectedFiltersData[i].filterName === FilterNameType.PageSkip) {
const skip = parseInt(selectedFiltersData[i].value)
gridInstance.pageSettings.currentPage = skip / 20 + 1
}
}
}
gridInstance.pageSettings.totalRecordsCount =
props.MpVehiclesResponse.count
let rowIndex = -1
const rowIndexes: number[] = []
const selectedVehicleIds = mpVehicleData.map(
(vehicle: MpVehicle) => vehicle.uniqueIdentity
)
gridInstance.currentViewData.forEach((row: any, index) => {
if (selectedVehicleIds.includes(row.uniqueIdentity)) {
rowIndex = index
rowIndexes.push(rowIndex)
}
})
gridInstance.selectRows(rowIndexes)
}
}
const rowDataBound = (args: RowDataBoundEventArgs): void => {
const vehicleData: any = args.data
const selectedVehicleIds = mpVehicleData.map(
(vehicle: MpVehicle) => vehicle.uniqueIdentity
)
if (
selectedVehicleIds.length === vehicleSelectionLimit &&
!selectedVehicleIds.includes(vehicleData.uniqueIdentity)
) {
args.row
?.getElementsByClassName('e-gridchkbox')[0]
.classList.add('disablecheckbox')
args.row
?.getElementsByClassName('e-checkbox-wrapper')[0]
.classList.add('disablecheckbox')
}
}
useEffect(() => {
if (gridInstance) {
gridInstance.sortSettings = {
columns: [{ field: 'make', direction: 'Ascending' }],
}
if (selectedFiltersData.length > 0) {
for (let i = 0; i < selectedFiltersData.length; i++) {
if (selectedFiltersData[i].filterName === FilterNameType.OrderBy) {
gridInstance.sortSettings = {
columns: [],
}
const sortOrderData = selectedFiltersData[i].displayValue.split('|')
const direction =
sortOrderData[1] === 'Ascending' ? 'Ascending' : 'Descending'
const sortSettings: SortSettingsModel = {
columns: [
{
field: sortOrderData[0],
direction: direction,
},
],
}
gridInstance.sortSettings = sortSettings
//gridInstance.setProperties({ sortSettings: sortSettings })
}
}
}
}
})
return (
<StyledVehicleGrid>
<div className="ej-grid-header">
<p>
List of vehicles
<span
id="multiVehicleResult"
style={{ padding: '2px' }}
className="ng-star-inserted"
>
Result :{props.MpVehiclesResponse.count} vehicles
</span>
</p>
</div>
<GridComponent
id="grid"
ref={(g) => (gridInstance = g)}
dataSource={gridData}
loadingIndicator={{ indicatorType: 'Shimmer' }}
allowPaging={true}
allowSorting={true}
allowSelection={true}
allowGrouping={true}
editSettings={editOptions}
selectionSettings={selectionSettings}
pageSettings={pageSettings}
// sortSettings={sortingOptions}
rowSelected={rowSelected}
rowDeselected={rowDeselected}
actionBegin={actionBegin}
rowDataBound={rowDataBound}
dataBound={dataBound}
>
<ColumnsDirective>
<ColumnDirective type="checkbox" width="50" />
<ColumnDirective
field="vehicleId"
isPrimaryKey={true}
width="100"
textAlign="Left"
visible={false}
/>
<ColumnDirective field="uniqueIdentity" width="100" visible={false} />
<ColumnDirective
field="make"
headerText="Make"
width="100"
textAlign="Left"
/>
<ColumnDirective
field="model"
headerText="Model"
width="100"
textAlign="Left"
/>
<ColumnDirective
field="versionName"
headerText="Version"
width="200"
textAlign="Left"
/>
<ColumnDirective
field="bodyTypeDisplayValue"
headerText="Body Style"
width="100"
textAlign="Left"
/>
<ColumnDirective
field="fuelTypeDisplayValue"
headerText="Fuel type"
width="100"
textAlign="Left"
/>
<ColumnDirective
field="litres"
headerText="Engine Litres"
width="100"
textAlign="Left"
/>
<ColumnDirective
field="drivenWheelsDisplayValue"
headerText="Driven Wheels"
width="100"
textAlign="Left"
/>
<ColumnDirective
field="priceDisplayValue"
headerText="Price"
width="100"
/>
</ColumnsDirective>
<Inject services={[Sort, Page, Selection, Edit]} />
</GridComponent>
<JATOToastContainer
autoClose={2000}
closeOnClick={true}
draggable={true}
hideProgressBar={true}
newestOnTop={true}
pauseOnFocusLoss={false}
pauseOnHover={true}
position="top-center"
style={{ width: 'auto' }}
/>
</StyledVehicleGrid>
)
}
Get this error
Hi Rahul Rai,
From your shared error, we suspect that you are refreshing/updating the state while the sorting action is in progress. To validate further could you please share the below details which will be helpful for us to provide a better solution as early as possible?
Also, from your code, we could see that you are performing your own Grid actions like paging, and sorting. For this, we suggest the “Custom Binding” feature which provides an option for handling the Grid actions using your own logic. So please refer to the below suggestion and check whether it meets your requirements.
For every grid action (such as Filter, Page, etc.,), we have triggered the dataStateChange event and, in that event arguments we have sent the corresponding action details(like skip, take, filter field, value, sort direction, etc.,) based on that, you can perform the action in your service and return the data as a result and count object.
Note: ‘dataStateChange’ event is
not triggered at the Grid initial render. If you are using a remote service,
you need to call your remote service manually with a pagination query (need
to set skip value as 0 and take value
based on your pageSize of pageSettings in
Grid. If you are not defined pageSize in pageSettings,
you need to send the default value 12 ) in load event of
Grid. Please return the result like as “{result: […], count: …}” format
to
Grid.
‘dataSourceChanged’ event is triggered when performing CRUD action in Grid. You can perform the CRUD action in your service using action details from this event and, you need to call the endEdit method to indicate the completion of the save operation.
Custom-binding: https://ej2.syncfusion.com/react/documentation/grid/data-binding/data-binding/#custom-binding
Demo: https://ej2.syncfusion.com/react/demos/#/material/grid/custom-binding
Regards,
Pavithra S
Hi there,
Please find required detail below:
function onSortChangeEvent(event: ActionEventArgs): void {
if (event.requestType === 'sorting') {
const customFields: string[] = Object.keys(customGridFields)
if (event.columnName) {
const columnName =
customFields.indexOf(event.columnName) !== -1
? customGridFields[event.columnName]
: event.columnName
dispatch(
addFilter({
key: uuid(),
displayValue: `${columnName}|${event.direction}`,
value: `${columnName} ${
event.direction === 'Ascending'
? MpVehicleOrderDirection.Ascending
: MpVehicleOrderDirection.Descending
}`,
filterName: FilterNameType.OrderBy,
singularFilterCategory: true,
filterParentType: FilterParentType.SpecsFilter,
})
)
}
}
}
const { data: mpVehiclesResponse, isFetching } = useQuery
[
'mpVehiclesResponse',
selectedFiltersData,
userData.accessToken,
mpReportMetaData,
],
async () => {
if (selectedFiltersData.length > 0) {
if (selectedFiltersData[i].filterName === FilterNameType.OrderBy) {
mpVehiclesSearchRequest.orderBy = []
mpVehiclesSearchRequest.orderBy.push(selectedFiltersData[i].value)
}
}
//"/(BMW:IX|JEEP:COMPASS)/" search param to be like this for make/models
if (makeFilter.length > 0 || modelFilter.length > 0) {
// Make and model search explainer:
// If only makes are selected, we need to search by all selected makes, with search queries like: /(BMW:|JEEP:)/
// If models are selected for a particular make, we can search in those selected models too with a pattern following: /(BMW:IX|JEEP:COMPASS)/ etc.
const makeModelSearchList: string[] = []
makeFilter.forEach((make) => {
let searchQuery = `${make.value}:`
if (make.subOptions !== undefined) {
// Find the models related to this make that are selected
const models = innerJoinModels(
getModelFilterOptions(make.value),
modelFilter,
make.value
)
if (models.length > 0) {
searchQuery = models.join('|')
}
}
makeModelSearchList.push(searchQuery)
})
const pipedMakeModelSearchQuery = makeModelSearchList.join('|')
mpVehiclesSearchRequest.search = `/(${pipedMakeModelSearchQuery})/`
}
}
if (
userData.accessToken.tokenValidUntil != undefined &&
userData.accessToken.tokenValidUntil < Date.now() / 1000
) {
const getAccessToken = axios.get
`${mpbaseAPIUrlNew}/api/AccessToken/get`,
{
headers: {
ContentType: 'application/json',
},
}
)
dispatch(renewAccessToken((await getAccessToken).data))
}
if (
userData.user.settings.lastSelectedMarket?.countryCode !== undefined
) {
const { data } = await monthlyPaymentsReportService.getMpVehicleData(
mpMakeModelSearchRequest,
userData.accessToken
)
setMakeModelData(data?.vehicles)
}
const { data } = await monthlyPaymentsReportService.getMpVehicleData(
mpVehiclesSearchRequest,
userData.accessToken
)
// Convert values to display values based on API metadata mapping
data.vehicles.forEach((vehicle: MpVehicle) => {
const fuelTypeDisplayValue = findAllowedValue(
mpReportMetaData?.metadata.fuelType?.allowedValues,
vehicle.fuelType
)
const bodyTypeDisplayValue = findAllowedValue(
mpReportMetaData?.metadata.bodyType?.allowedValues,
vehicle.bodyType
)
const drivenWheelsDisplayValue = findAllowedValue(
mpReportMetaData?.metadata.drivenWheels?.allowedValues,
vehicle.drivenWheels
)
if (fuelTypeDisplayValue != undefined) {
vehicle.fuelTypeDisplayValue = fuelTypeDisplayValue.toUpperCase()
}
if (bodyTypeDisplayValue != undefined) {
vehicle.bodyTypeDisplayValue = bodyTypeDisplayValue.toUpperCase()
}
if (drivenWheelsDisplayValue != undefined) {
vehicle.drivenWheelsDisplayValue = drivenWheelsDisplayValue.toUpperCase()
}
vehicle.priceDisplayValue = formatCurrency(
vehicle.price,
vehicle.country
)
})
return data
}
)
"@syncfusion/ej2": "^20.3.56",
"@syncfusion/ej2-react-buttons": "^20.3.57",
"@syncfusion/ej2-react-calendars": "^20.3.56",
"@syncfusion/ej2-react-dropdowns": "^20.3.57",
"@syncfusion/ej2-react-grids": "^20.3.56",
"@syncfusion/ej2-react-inputs": "^20.3.57",
"@syncfusion/ej2-react-layouts": "^20.3.57",
"@syncfusion/ej2-react-navigations": "^20.3.57",
"@syncfusion/ej2-react-popups": "^20.3.57",
Thanks,
Rahul
Hi Rahul Rai,
Thanks for sharing the details.
Since you want to perform the custom sorting in Grid, we suggest canceling the default sort action inside the “actionBegin” event to overcome the reported issue. Please refer to the below code example for more information.
const actionBegin = (args: ActionEventArgs): void => {
if (args.requestType === 'sorting') {
args.cancel = true
props.onSortChangeEvent(args)
}
}
|
Regards,
Pavithra S