hyojin / material-ui-datatables

An another React Data tables component.
MIT License
165 stars 58 forks source link

Column sorting does not work #5

Open mantydze opened 7 years ago

mantydze commented 7 years ago

Sorting in example does not work

advance512 commented 7 years ago

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.

mantydze commented 7 years ago

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

hyojin commented 7 years ago

@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!

ghost commented 7 years ago

@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. :)

advance512 commented 7 years ago

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:       () => {
  },
};
hyojin commented 7 years ago

@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

ghost commented 7 years ago

@hyojin @advance512 Thanks for the quick reply.

ekafyi commented 7 years ago

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!

hyojin commented 7 years ago

@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.