AllenFang / react-bootstrap-table

A Bootstrap table built with React.js
https://allenfang.github.io/react-bootstrap-table/
MIT License
2.23k stars 782 forks source link

Ajax call in the expand row #1035

Open Gi972 opened 7 years ago

Gi972 commented 7 years ago

Hello @AllenFang

I want when I expand a row, make a ajax's call and return a content, maybe a other table or other div. Or in your example your expand props the data is already loaded. I think it's better to make a call ajax to fill the expand row.

It's possible? or have you a idea for implement this? or have you got a example? thanks

AllenFang commented 7 years ago

@Gi972, I will support this feature, I want separate the content as two kind of usage, one is for the data loading and the other is for the real content after data loading.

AllenFang commented 7 years ago

BTW, I'm not sure when this feature will be supported, because I'm busy on my job and ready to release for v3.0.0, so maybe pending to v3.0.0 or on v2.11.3.

phamj88 commented 7 years ago

I also agree that this will be a good feature. It lets you be lazy with expanding rows. In the use case you are doing an async call for each row, it could reduce server calls.

THanks for the great library by the way!

AllenFang commented 7 years ago

hi all, I've some troubles on implement this feature and after I investigate further, I've some feedback want to give you guys.

For the async content, maybe you can achieve with following

AsyncExpandContent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
        expandingContentId: []
    };
  }

  isExpandableRow(row) {
    if (row.id < 3) return true;
    else return false;
  }

  expandComponent(row) {
    if (this.state.expandingContentId.indexOf(row.id) === -1) {
        setTimeout(() => {  // here is just a simulative AJAX call
           // after AJAX complete::: save your content and force update the expandingContentId state
          const expandingContentId = this.state.expandingContentId;
          expandingContentId.push(row.id);
          this.setState({ expandingContentId });
        }, 10000);
    } else {
        return (
           // your content
        );
    }
  }

  render() {
    const options = {
      expandRowBgColor: 'rgb(242, 255, 163)'
    };
    return (
      <BootstrapTable ref='table'
        data={ products }
        options={ options }
        striped
        expandableRow={ this.isExpandableRow }
        expandComponent={ this.expandComponent }
        search>
        <TableHeaderColumn dataField='id' isKey={ true }>Product ID</TableHeaderColumn>
        <TableHeaderColumn dataField='name'>Product Name</TableHeaderColumn>
        <TableHeaderColumn dataField='price'>Product Price</TableHeaderColumn>
      </BootstrapTable>
    );
  }
}

Anyway, I'll keep to find some solution, bot I want to hear you guys feedback, thanks

Gi972 commented 7 years ago

Hi @AllenFang ,

I don't know if is the most elegant, but It's not a bad idea. I will test it.

Gi972 commented 7 years ago

How you would like to implement it if you had no problem? with a new props like expandableAsyncRow ?

AllenFang commented 7 years ago

How you would like to implement it if you had no problem? with a new props like expandableAsyncRow ?

Good question, that's actually my problem, I wanna expose an API for you so that your AJAX call complete then call this exposed API, but there's something stuck.. If use a props is also a good solution, you need to save your result data in somewhere and the other problem is I dont know when you complete the AJAX~

Gi972 commented 7 years ago

Hi @AllenFang,

Arff! I tested and I had forgotten on the expanComponent you loop on all rows . So the ajax's call doesn't make on the click for a specific row but make a call on all rows and this is what we want to avoid. Maybe you should create a spécific props when you click on one row... lol in fact like onSelectRow...

Gi972 commented 7 years ago

I try with onSelectRow callback it works, it 's a bit messy but it works...

So Norw I don't know if you want create a new props or use the onSelectRow Call Back.

This is the code:


import React from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
import $ from 'jquery';

const products = [];

function addProducts(quantity) {
  const startId = products.length;
  for (let i = 0; i < quantity; i++) {
    const id = startId + i;
    if (i < 3) {
      products.push({
        id: id,
        name: 'Item name ' + id,
        price: 2100 + i,
        expand: null
      });
    } else {
      products.push({
        id: id,
        name: 'Item name ' + id,
        price: 2100 + i
      });
    }
  }
}
addProducts(5);

export default class AsyncExpandContent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { expandingContentId: [] };
  }

 addAsyncProduct(row) {
   const root = 'http://jsonplaceholder.typicode.com';
   console.log('async');
   $.ajax({
     url: root + '/users',
     method: 'GET'
   }).then((data)=> {
     console.log('data', data);
     products[row.id].expand = {
       email: data[row.id].email,
       name: data[row.id].name,
       phone: data[row.id].phone
     };

     const expandingContentId = this.state.expandingContentId;
     expandingContentId.push(row.id);
     this.setState({ expandingContentId });
   });
 }

  isExpandableRow(row) {
    if (row.id > 1) return true;
    else return false;
  }

  expandComponent(row) {
    console.log('row', row);

    if (this.state.expandingContentId.filter((el) => {
      return el === row.id;
    }).length > 0) {
      return (<div>
            <ul>
              <li>{ row.id }</li>
              <li>{ row.price }</li>
              <li>{ row.expand.email }</li>
              <li>{ row.expand.name }</li>
              <li>{ row.expand.phone }</li>
              </ul>
        </div>);
    }
  }

  render() {
    const options = {
      expandRowBgColor: 'rgb(242, 255, 163)'
    };

    const selectRow = {
      mode: 'checkbox',  // multi select
      bgColor: 'pink',
      onSelect: this.addAsyncProduct.bind(this),
      hideSelectColumn: true,
      clickToExpand: true,
      clickToSelect: true
    };
    return (
      <BootstrapTable ref='table'
        data={ products }
        options={ options }
        striped
        selectRow={ selectRow }
        expandableRow={ this.isExpandableRow }
        expandComponent={ this.expandComponent.bind(this) }
        search>
        <TableHeaderColumn dataField='id' isKey={ true }>Product ID</TableHeaderColumn>
        <TableHeaderColumn dataField='name'>Product Name</TableHeaderColumn>
        <TableHeaderColumn dataField='price'>Product Price</TableHeaderColumn>
      </BootstrapTable>
    );
  }
}

It's not perfect , I think we can better... Give me your opinion.

Gi972 commented 7 years ago

The advantage of this, is you can use any libraries for ajax call... You not need implement a ajax call in bootstrapt Table, but you manage from A to Z.

AllenFang commented 7 years ago

sure, thanks your feedback, I'll keep to find out a best solution 👍 Thanks!

dekelb commented 7 years ago

Since v3.3.5 there is the options.onExpand callback which gives a pretty easy way to do async loading of data in the content of the expanded row. I don't think we should have a special feature for that in this lib, since it's pretty easy to do it with the callback.

ajits731 commented 6 years ago

Any updates on this issue? Still struggling to execute this.

dekelb commented 6 years ago

@ajits731 what exactly is the problem?

DanielHosseini commented 6 years ago

So a bit late, but is it still not possible to do a ajax call and based in the response, render the expanded view?

sudarshantanwer commented 6 years ago

I am also facing the same issue. I want to trigger an async call and then want to show data in expanded table. But as of now this is being executed for each row on table initialisation, but I want this should happen only for specific row which I clicked on. Any updates ?