Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I create a custom link formatter? #135

Open
8483 opened this issue Oct 27, 2019 · 11 comments
Open

How can I create a custom link formatter? #135

8483 opened this issue Oct 27, 2019 · 11 comments
Labels

Comments

@8483
Copy link

8483 commented Oct 27, 2019

I cannot for the life of me get the formatter to work.

I get blank cells instead of a formatted link.

The React documentation could really use some explanations.

Side-question: Can I use the vanilla Tabulator in React, instead of the adapted one?

import React, { useState, useEffect } from "react";
import { Link } from 'react-router-dom';
import "tabulator-tables/dist/css/tabulator.min.css";
import { ReactTabulator } from 'react-tabulator'

function Journals(props) {

    const [journals, setJournals] = useState([]);
    const [inputs, setInputs] = useState([]);

    useEffect(() => {
        fetch("http://localhost:4000/journals")
            .then(res => res.json())
            .then(data => {
                setJournals(data)
            })
            .catch(err => err);
    }, []);

    const journalLinkFormatter = (cell, formatterParams) => {
        let key = cell.getValue()
        let link = `/journals/${key}`
        return <Link style={{ color: "blue", fontWeight: "bold", background: "red" }} to={link}>{key}</Link>
    }

    const columns = [
        { title: "Number", field: "key_", formatter: journalLinkFormatter },
        { title: "Date", field: "date_" },
    ];

    return (
        <div>
            <h1>Journals</h1>
            <ReactTabulator
                data={journals}
                columns={columns}
                tooltips={true}
                layout={"fitData"}
            />
        </div >
    )
}

export default Journals;

This way works, but it beats the purpose, as it reloads everything.

const columns = [
        {
            title: "Number", 
            field: "key_", 
            formatter: "link", 
            formatterParams: { url: cell => { return "/journals/" + cell.getValue() } }
        },
        { title: "Date", field: "date_" },
    ];

I'd like to learn how to use custom formatters.

@ngduc
Copy link
Owner

ngduc commented Oct 27, 2019

@8483
Copy link
Author

8483 commented Oct 30, 2019

How do I make your example work in my case. I really have no idea.

I am getting a Link outside a Router error, for no reason. Any advice?

https://stackoverflow.com/questions/58645310/link-outside-a-router-error-while-everything-set-up-properly

Here's a CodeSandbox that demonstrates the problem.

@8483
Copy link
Author

8483 commented Oct 31, 2019

I got this reply on StackOverflow.

react-tabulator reFormatter is incompatible with react-router library.

https://github.com/ngduc/react-tabulator/blob/0.10.3/lib/Utils.js#L30

From source code,

function reactFormatter(JSX) {
    return function customFormatter(cell, formatterParams, onRendered) {
        //cell - the cell component
        //formatterParams - parameters set for the column
        //onRendered - function to call when the formatter has been rendered
        onRendered(function () {
            var cellEl = cell.getElement();
            var CompWithMoreProps = React.cloneElement(JSX, { cell: cell });
            react_dom_1.render(CompWithMoreProps, cellEl.querySelector('.formatterCell'));
        });
        return '<div class="formatterCell"></div>';
    };
}

rendering of a formatted element uses the ReactDOM.render function to render the formatted element directly to DOM isolated from parent elements.

A fix to react-tabulator needs to be done to support this use case. One way to go is to have customFormatter return a custom component that provides a way to set its state from outside it. Then onRendered can call this function to set cell.

@ngduc
Copy link
Owner

ngduc commented Nov 14, 2019

@8483 I will look into this to see any alternative to ReactDOM.render
If anyone can help, it'll be great.

@ngduc ngduc added the todo label Nov 14, 2019
@8483
Copy link
Author

8483 commented Nov 14, 2019

Thank you for considering it! It really is a must have.

@ericnkatz
Copy link
Contributor

Can we use portals for this instead?

@ericnkatz
Copy link
Contributor

As of right now anytime you interact with the Table in a way that triggers reload (sorting by column for instance) it continually instantiates and appends a completely unique React instance to the DOM. Sort two or three times and all of a sudden your 10 rows with 2-3 custom ReactFormatted cells are now hundreds of mounted and abandoned React DOM/rendered instances.

@ngduc
Copy link
Owner

ngduc commented Apr 9, 2020

@8483 @ericnkatz
I fixed the reactFormatter so it can re-render on cell edit, new version 0.12.0.

will look into react dom & react-router.

@cristian-spiescu
Copy link

Cf. this react doc, shouldn't there be an "opposite" action of ReactDOM.render()? I.e. call ReactDOM.unmountComponentAtNode() when the cell is disposed, so that some cleanup is done?

It's normal that Link doesn't work. The Router instance from the main react tree needs to be somehow stored as a global plain JS var and passed to tabulator. And then, within the custom renderer, provided via Context. This way, Link retrieves the Router instance.

@cristian-spiescu
Copy link

I have created an implementation using portals in this gist. It's a Storybook pages. I'm using semantic-ui-react in my project, so a copy/paste of the file may need adapting.

I don't know if it's OK to have thousands of portals. I.e. one per cell. Also, I didn't see any action do "dispose" a portal. I hope that disposing the DOM element where the portal was rendered suffices. Otherwise => memory leaks possible I think.

@Maxssobolev
Copy link

Maxssobolev commented Aug 31, 2021

Easy way is conditional rendering all page with tabulator
So, this way warked for me:

const [redirect, setRedirect] = useState({ allow: false })
const columns = [
        { title: 'No', field: 'no', },
        {
            title: 'Patient', field: 'patient', cellClick: function (e, cell) {
                const rowInfo = cell._cell.row.data
                setRedirect({ allow: true, id: rowInfo.id }) //here we update state
            }
        },
]
//....
if (redirect.allow) {
        return <Redirect to={{
            pathname: '/accounts/profile',
            state: {
                id: redirect?.id
            }
        }} />
    } else {
   //...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants