Need help for infinite scroll pagination in the datatable for a react project

Hello,

I developing React webix datatable with the data being fetched from REST API.
https://api.skuops.com/products?pagesize=10&startindex=0&sorting=-title

I have to implement the pagination while scrolling down the datatable through means of sending the next set of startindex to the REST API. Also I need to maintain the sorting and filtering with this.
Second set: https://api.skuops.com/products?pagesize=10&startindex=10

I am not using any server side code here. I tried to search the docs and didn’t get an idea how to implement this feature.

Could you please guide me to implement this functionality?

Thanks,
Shan

you need to implement own data loading logic.
listen to onAfterScroll, onBeforeSort, onBeforeFilter events and load related data.
you will need to block table while data is loading.
after load next portion append it to existing data.

Yes. I am using the following code snippet where the scroll(scroll: true,onScrollY) event is not triggering at all.


import styles from './productslist.cssm';

import React, { Fragment } from 'react';
import { connect } from 'utils/connect';
import { List } from 'immutable';
import { fromJS } from 'immutable';

import Webix from 'components/Webix';

import * as SKUEntity from 'entities/SKUEntity';
import ImageModal from './comps/ImageModal';

function getSkuFilter(props) {
    return {
        filters: {
            populate: 'variants',
            pagesize: 20,
            startindex: 0,
        },
    };
}

@connect('sku', SKUEntity, getSkuFilter)
export default class ProductsList extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            expanded: {},
            ui: fromJS({
                view: 'datatable',
                id: 'grid',
                autoConfig: true,
                scroll: true,
                select: false,
                height: 200,
                autoheight: true,
                autowidth: true,
                width: '100%',
                multiselect: true,
                editable: true,
                editaction: 'dblclick',
                subview: {
                    view: 'datatable',
                    columns: [{ id: 'name', sort: 'string', fillspace: true }, { id: 'type' }],
                    data: [
                        { name: 'North', January: 100, February: 230, March: 180 },
                        { name: 'West', January: 70, February: 120, March: 160 },
                    ],
                },
                columns: [],
            }),
        };
    }
    componentDidMount() {
        this.fetchProducts(this.props);
    }

    fetchProducts = (props = this.props) => {
        props.sku.actions.get(getSkuFilter(props));
    };

    getColumns = props => {
        let columns = props.sku.metadatas.getIn([0, 'columns']);
        columns = columns
            .map(column => {
                switch (column.get('type')) {
                    case 'image':
                        return {
                            id: column.get('name'),
                            header: column.get('name'),
                            width: 200,
                            accessor: column.get('key').toJS(),
                            type: column.get('type'),
                            template: obj => {
                                const images = obj[column.get('name')];
                                return `<span>${
                                    images.length
                                        ? images.map(
                                              img =>
                                                  "<imsd class=${styles.productImages} src="${
                                                      img.src
                                                  }" />"
                                          )
                                        : '--'
                                }</span>`;
                            },
                        };
                    default:
                        return {
                            id: column.get('name'),
                            header: column.get('name'),
                            css: { 'text-align': 'center' },
                            accessor: column.get('key').toJS(),
                            editor: column.get('isEditable') ? column.get('type') : '',
                            onChange: () => {
                                console.log(arguments);
                            },
                        };
                }
            })
            .toJS();

        columns.unshift({
            id: 'idx',
            header: '',
            template: '{common.checkbox()}',
            css: { 'text-align': 'center' },
            width: 50,
            accessor: ['id'],
        });
        return columns;
    };

    componentWillReceiveProps(nextProps) {
        if (!this.props.sku.status.isSynced && nextProps.sku.status.isSynced) {
            const columns = this.getColumns(nextProps);

            const tableRows = nextProps.sku.value.map((row, i) => {
                let rowsData = {
                    idx: { id: i + 1, onSelect: this.onSelect, isSelected: true },
                };
                columns.forEach(column => {
                    const cellData = row.getIn(column.accessor);
                    if (List.isList(cellData)) {
                        rowsData[column.id] = row.getIn(column.accessor).toJS();
                    } else {
                        rowsData[column.id] = row.getIn(column.accessor);
                    }
                });
                if (row.get('variants') && row.get('variants').size) {
                    rowsData.children = row
                        .get('variants')
                        .map((variant, index) => {
                            const subRowData = {};
                            columns.forEach(column => {
                                subRowData[column.id] = variant.getIn(column.accessor);
                            });
                            return subRowData;
                        })
                        .toJS();
                }
                return rowsData;
            });
            this.setState({
                rows: tableRows.toJS(),
                ui: this.state.ui.set('columns', columns),
            });
        }
    }

    render() {
        const { selectedItem } = this.state;
        let Comp;

        if (selectedItem) {
            switch (selectedItem.type) {
                case 'image':
                    Comp = (
                        <ImageModal
                            onClose={this.onClose}
                            data={selectedItem.data}
                            label={selectedItem.label}
                        />
                    );
                    break;
            }
        }

        return (
            <div style={{ width: '100%' }}>
                {this.props.sku.status.isSynced ? (
                    <Webix
                        ui={this.state.ui.toJS()}
                        data={this.state.rows}
                        onItemClick={this.onItemClick}
                        onScrollY={this.onScrollY}
                        onEditorChange={this.onEditCell}
                        events={[
                            'onItemClick',
                            'onScrollY',
                            'onChange',
                            'onEditorChange',
                            'onCollectValues',
                        ]}
                    />
                ) : null}
                {selectedItem ? Comp : null}
            </div>
        );
    }

    onClose = () => {
        this.setState({
            selectedItem: null,
        });
    };

    onScrollY = () => {
        console.log(arguments);
    };

    onEditCell = (id, value, table) => {
        console.log(id);
        console.log(value);
        const row = table.getItem(id.row);
        console.log(row);
    };

    onItemClick = (id, e, trg, table) => {
        const { ui } = this.state;
        const column = ui.get('columns').find(col => col.id === id.column);

        if (column.type === 'image') {
            this.setState({
                selectedItem: {
                    type: column.type,
                    label: column.header[0].text,
                    data: table.getItem(id.row)[id.column],
                },
            });
        }
    };
}


If you can know the total count of record the task is simple. Just provide the total_count attribute for the initial data

const data = {
  total_count: 5000,
  data:[
    ... initial data here ...
  ]
}

Now, you can catch the onDataRequest event
https://docs.webix.com/api__link__ui.proto_ondatarequest_event.html
return false from the handler to block default data loading and issue the custom query to fetch the necessary data. Call grid.parse(new_data) when the data is available.