material-ui-datatables icon indicating copy to clipboard operation
material-ui-datatables copied to clipboard

Column sorting does not work

Open mantydze opened this issue 8 years ago • 9 comments

Sorting in example does not work

mantydze avatar Feb 15 '17 16:02 mantydze

Column sorting in example isn't meant to work - it isn't implemented. It is up to the user to sort the data and re-render following the change of props.

advance512 avatar Mar 13 '17 03:03 advance512

In that case the name of the library is misleading... Datatables usually provide sorting out of the box.

mantydze avatar Mar 13 '17 08:03 mantydze

@advance512 Thanks for your comment! @mantydze Yes, you are right! most of Datatables component provide features like sorting are done within the library. but on the other hand, in react and flux(redux) world, it means you will lose some of your controls of components. so far, I don't know which is the right way. So, you can choose! :) there are a lot of cool react datatables already!

hyojin avatar Mar 14 '17 02:03 hyojin

@hyojin
Could you please provide the demo source code, so it might help us to see how you implement sorting, pagination and search functionality. Right now the examples are not working as demo. :)

ghost avatar Apr 19 '17 13:04 ghost

Here is how I use the this component. I have placed it in another component so I can easily change to another library, if I run into a wall.

Haven't done sorting yet, but as you can see it should be quite easy. Look at the filtering example, but instead of filter - use Array.prototype.sort().

/* eslint-disable class-methods-use-this */
import React, { PropTypes } from 'react';
import DataTables from 'material-ui-datatables';
import { RefreshIndicator } from 'material-ui';
import get from 'lodash/get';

export class DataTable extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      page:        1,
      rowSize:     10,
      filterValue: null,
    };

    this.handleCellClick = ::this.handleCellClick;
    this.handleCellDoubleClick = ::this.handleCellDoubleClick;
    this.handleFilterValueChange = ::this.handleFilterValueChange;
    this.handleSortOrderChange = ::this.handleSortOrderChange;
    this.handleRowSizeChange = ::this.handleRowSizeChange;
    this.handlePreviousPageClick = ::this.handlePreviousPageClick;
    this.handleNextPageClick = ::this.handleNextPageClick;
  }

  handlePreviousPageClick() {
    const newState = Object.assign({}, this.state, { page: this.state.page - 1 });
    this.setState(newState);
  }

  handleNextPageClick() {
    const newState = Object.assign({}, this.state, { page: this.state.page + 1 });
    this.setState(newState);
  }

  handleRowSizeChange(rowSizeIndex, rowSize) {
    const newState = Object.assign({}, this.state, { page: 1, rowSize });
    this.setState(newState);
  }

  handleCellClick(tableRow, tableColumn, dataItem, dataItemField) {

    const { handleClick } = this.props;

    // eslint-disable-next-line no-console
    // console.log('handleCellClick', dataItem, dataItemField);

    handleClick(dataItem, dataItemField);
  }

  // eslint-disable-next-line no-unused-vars,no-unused-vars-rest/no-unused-vars
  handleCellDoubleClick(tableRow, tableColumn, dataItem, dataItemField) {
  }

  handleFilterValueChange(...args) {
    // eslint-disable-next-line no-console
    let filterValue = get(args, '[0]', null);
    if (filterValue) {
      filterValue = filterValue.toLowerCase();
    }
    const newState = Object.assign({}, this.state, { filterValue });
    this.setState(newState);
  }

  handleSortOrderChange(...args) {
    // eslint-disable-next-line no-console
    console.log('SortOrderChange', args);
  }

  render() {

    const { loading, tableColumns, dataItems, dataItemFormatter } = this.props;

    if (loading) {
      return (
        <div>
          <RefreshIndicator
            size={64}
            left={10}
            top={10}
            status="loading"
            style={{
              position: 'relative',
            }}
          />
        </div>
      );
    } else {

      // Only filter if required
      let filteredItems = dataItems;

      // Filter to select only the items that pass our seach, but only in the selected columns
      if (this.state.filterValue) {
        filteredItems = filteredItems.filter((item) => {

          for (const currentColumn of tableColumns) {
            if (get(item, currentColumn.key, '').toString().toLowerCase().includes(this.state.filterValue)) {
              return true;
            }
          }

          return false;
        });
      }

      // TODO: Prime reselect location right here
      const formattedDataItems =
        filteredItems
          .slice(this.state.rowSize * (this.state.page - 1), this.state.rowSize * (this.state.page))
          .map(dataItemFormatter);

      return (
        <DataTables
          height={'auto'}
          selectable={false}
          showRowHover
          columns={tableColumns}
          data={formattedDataItems}
          showCheckboxes={false}
          showHeaderToolbar
          onPreviousPageClick={this.handlePreviousPageClick}
          onNextPageClick={this.handleNextPageClick}
          onRowSizeChange={this.handleRowSizeChange}
          onCellClick={this.handleCellClick}
          onCellDoubleClick={this.handleCellDoubleClick}
          onFilterValueChange={this.handleFilterValueChange}
          onSortOrderChange={this.handleSortOrderChange}
          count={dataItems.length}
          page={this.state.page}
          rowSize={this.state.rowSize}
        />
      );
    }
  }
}

DataTable.propTypes = {
  loading:           PropTypes.bool,
  tableColumns:      PropTypes.arrayOf(PropTypes.shape({
    key:   PropTypes.string,
    label: PropTypes.string,
  })),
  dataItems:         PropTypes.arrayOf(PropTypes.object),
  dataItemFormatter: PropTypes.func,
  handleClick:       PropTypes.func,
};

DataTable.defaultProps = {
  loading:           true,
  tableColumns:      [
    {
      key:   'name',
      label: 'Name',
    },
  ],
  dataItems:         [],
  dataItemFormatter: item => item,
  handleClick:       () => {
  },
};

advance512 avatar Apr 19 '17 20:04 advance512

@advance512 Thank you for giving us your code! :) @bharadwajag It depends on your data and how you want to implement, but if you are thinking sorting on client side, I think using lodash is a good option. https://lodash.com/docs/4.17.4#sortBy

And also you can find my demo code on gh-pages branch, https://github.com/hyojin/material-ui-datatables/tree/gh-pages/demo/src

hyojin avatar Apr 20 '17 00:04 hyojin

@hyojin @advance512 Thanks for the quick reply.

ghost avatar Apr 20 '17 04:04 ghost

Hi, I went through your example code but did not understand how to implement it (React novice here). I fetch my data from an external API, store it in props (eg. this.props.foo.posts), then map it into table rows.

Could you please help point out how to modify the FakeApi function in the code below?

FakeAPI(this.state.currentPage, this.state.rowSize, key, order, this.state.filter, (result) => {
	this.setState({
		total: result.count,
		data: result.data,
		sort: key,
		order: order,
	});
});

Any help would be appreciated. Thanks!

ekafyi avatar Aug 24 '17 06:08 ekafyi

@ekaoddlass Hi, FakeAPI just slices data array based on currentPage and rowSize parameters and returns it. So if your api already has pagination, call your api, instead of FakeAPI, and just pass page and row size parameter and set the result as data prop. if it doesn't, you can fetch all data then manipulate as same as what FakeAPI does.

I'm not sure what is your problem exactly, if you have troubles with managing data on front-end side, I recommend you to look through the documents of Flux/Redux.

hyojin avatar Aug 28 '17 07:08 hyojin