react-bootstrap-table2 icon indicating copy to clipboard operation
react-bootstrap-table2 copied to clipboard

How to pass editable state into column formatters

Open JohnVLinton opened this issue 7 years ago • 14 comments

I am trying to implement a toggle button which has its own jsx inside one of the boostrap table's columns (for leftmost cell each row).

My issue is I can't seem to find a way to pass the parent's callback function from the react-bootstrap-table itself into a column formatter (i.e. there is no bind this allowed) such that a callback gets called from the parent that can change state based on whether someone hits the toggle button in each cell or not.

Basically my overall goal is to have some "parent rows" with expander button on left-hand most column and if a person hits that expander button it will change the state of a hiderow[] array of booleans and redraw the table data showing the once hidden child rows based on parent expansion. (These are basically the same display style as the parents, it's just that the parent expander button "uncoils them into view".)

Every other bit of the logic works in this pipeline except how to get a state-changing function passed to an external .jsx as a callback. Because as opposed to normal React parent-child interaction I have to somehow feed the callback function through the column formatter function, which seems to reside outside the React component that uses it (hence passing state functions not easy).

I have to go through the column formatter obviously because this toggle button appears on every single row and so has to be replicated, calling different ids, etc... I tried to use a generic column click event but lol can't get rowID from that nor grab the inner toggle imagebutton except for maybe low-level js. Similarly the row click event. (There does not appear to be a cell-click event but this too would need a further "zoom in" on the image button inside it).

JohnVLinton avatar Feb 27 '18 20:02 JohnVLinton

I'm also not able to update columns with dynamic data in the formatter: functions of column definition. Each time state gets updated the column formatter: functions were never run

resistancecanyon avatar Dec 03 '18 06:12 resistancecanyon

@JohnVLinton It is possible to do so.. formatters: are pure functions and you need to pass external state data using formatExtraData along with formatter please check doc below https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnformatextradata-any

resistancecanyon avatar Dec 03 '18 07:12 resistancecanyon

I have the same issue. if I set formatExtraData: this.props.teams and the data attached to this cell or row with isDummyField: true, the cell doesn't update when this.props.teams change (immutability respected). Do you have the same problem?

I could be wrong but I think that the issue is it updates only if the number of column change

https://github.com/react-bootstrap-table/react-bootstrap-table2/blob/9d2a6a1b23078be9426ce6130e8d156553600088/packages/react-bootstrap-table2/src/row/should-updater.js#L23-L31

It would be great to trigger rows update if formatExtraData is modified. Would this change work in should-updater.js? shouldComponentUpdate should be modified accordingly in cell.js too to track change on formatExtraData

    shouldUpdatedByNormalProps(nextProps) {
      const shouldUpdate =
        this.props.rowIndex !== nextProps.rowIndex ||
        this.props.editable !== nextProps.editable ||
        !_.isEqual(this.props.row, nextProps.row) ||
        this.props.columns !== nextProps.columns;

      return shouldUpdate;
    }

@AllenFang your thoughts?

clemlem avatar Dec 06 '18 21:12 clemlem

Same here, passing this.state.something into formatExtraData does not seem to re-run the formatter

pastinepolenta avatar Jul 05 '19 15:07 pastinepolenta

Did any of your find a workaround for this? I'm hitting the same issue right now. For now, I'm using this dirty little hack to get around it by setting a hidden field to a random value on the desired row. That way, it forces a re-render

this.setState({ 
    data: data.map(row => {
        if (row.id == id) {
            return { ...id, dirty: Math.random() }
        }

        return row;
    });
}

senecaso avatar Jul 21 '19 05:07 senecaso

Yes I believe there is a way we found (not right in front of me).

Key is for you to console.log() inside the formatter the objects and you should be able to find how to pass an additional property. You basically walk it out until you use up all the fields the library needs, (p1, p2, p3, ...) then the next space is where you can add your own, I think, if I recall right...

jvlinton avatar Jul 21 '19 14:07 jvlinton

@jvlinton ,Could you please provide an example for this..i need to show a tooltip on hover of a cell..stuck with the same issue,state is updated but formatter is not being invoked.

 `formatter: (cell, row, rowIndex, { showTooltip }) => {
      console.log('in cel', cell) // not being logged when showTooltip is changed
      console.log('tooltipOpen in formatter', showTooltip)
      const tags = cell.split(',').filter(a => a)
      console.log('tags', tags)
      if (tags.length <= 2) {
        console.log('in first')
        return <div>{cell}</div>
      } else {
        return (
          <div>
            <div id={`TooltipExample${row.id.replace(/\//g, '')}`}>{`${
              tags[0]
            },${tags[1]} + ${tags.length - 2} more`}</div>
            <Tooltip
              placement="right"
              isOpen={showTooltip}
              target={`TooltipExample${row.id.replace(/\//g, '')}`}
              toggle={this.toggleTooltip}
            >
              {`${tags.slice(2).join(',')}`}
            </Tooltip>
          </div>
        )
      }
    },
    formatExtraData: {
      showTooltip
    }`

toggleTooltip = () => {
console.log('in toggle')
this.setState(prevState => ({ showTooltip: !prevState.showTooltip }))

}

anuragb26 avatar Aug 21 '19 13:08 anuragb26

I had to re-render the component with a show/hide react condition after trying to solve it for hours. It used to work in previous version(^1.1) but after 2, i believe performance checks inside the component has made this update to not happen.

resistancecanyon avatar Aug 23 '19 07:08 resistancecanyon

The same issue for me unfortunately :/

EDIT #1: Upgrading to version 4.0.1 resolved my issue

DorienMay avatar May 05 '20 15:05 DorienMay

@DorienMay

Do you have a working example? I still can't get this to work even on 4.0.1...

TempleClause avatar May 20 '20 12:05 TempleClause

I'll try to find an example of the code... The secret to figuring out how is to console.log each of the things coming into the formatter, if I remember rightly, and keep adding params until you can add something new that the library isn't already sending in... Or else I think you can hack/overwrite the row object and return extra param or something like that

jvlinton avatar May 20 '20 13:05 jvlinton

Try: static keyword

  1. Bind the static method in constructor MyClass.showModal = MyClass.showModal.bind(this);

  2. Define the method static showModal() { this.setState({ show: true }) };

  3. Call the static method from Formatter method

const cellFormatter = () => { return ( <Button className={'btn btn-primary'} onClick={MyClass.showModal}>Attend</Button> ); };

adityaraj52 avatar Jun 15 '20 20:06 adityaraj52

First set a key on the bootstrap table. When you update the data which you're not seeing reformatted, also change the key name of the bootstrap table. That will force React to re-rendered the table and your formatter method will, just like magic, do its thing.

wiwsem avatar Sep 05 '20 02:09 wiwsem

did you find any solution for this ?

Hanh28111995 avatar Aug 16 '23 19:08 Hanh28111995