mui-datatables icon indicating copy to clipboard operation
mui-datatables copied to clipboard

customBodyRender a cell with multiple links from an array

Open POlsen-92 opened this issue 3 years ago • 0 comments

Hello!

This isn't a bug but me trying to figure out how to do something, if it's possible.

I am creating an app with a lot of relationships and I want to create links to different profiles within the cells. Sometimes a single cell may need to contain more than one link.

An example of this use case might be Pet owners and their pets. Say I create a table of Owners and then in one cell is a list of the pet names

Owners Pet Names
Paige Sally, Sage, Ember

I've gotten it to where I can push all the names of the animals related to a specific owner to a new array and then join that array to create a string that shows up properly with all the pets listed. When I click on the row I get taken to the owner profile with the onRowClick function which is exactly how I want it

Code Example

    const [data, setData] = useState([]);
    const [petColumns, setPetColumns] = useState([]);
    const [columnData, setColumnData] = useState([]);
    const [options, setOptions] = useState({
      filter: true,
      filterType: 'dropdown',
      responsive: 'standard',
      rowsPerPage: 10,
      resizableColumns: true,
      enableNestedDataAccess: '.',
      selectableRows: 'none',
      onRowClick: (rowData) => {
        handleClick(rowData);
      }
    })

setPetColumns = [
        {
              name: "id",
              label: "Id",
              options: {
                display: 'excluded',
               }
         },
         {
              name: "name",
              label: "Owner Name",
         },
         {
              name: "pet",
              label: "Pets",
         },
]

useEffect(() => {
    API.getOwnerData(ownerId)
        .then(res => setData(res.data))
        .catch(err => console.log(err))
}, [ownerId])

useEffect(() => {
    const ownerData = data.map((owner) => {
            const petList = []
            owner.Pets.forEach((pet) => {
              petList.push({
                name: pet.name,
              })
            })

            const petObject = {
              id: owner.id,
              name: `${owner.User.firstName} ${owner.User.lastName}`,
              pet: petList.map(p => p.name).join(', '),
            }
            return petObject
          })
   setColumnData(ownerData)
}, [data])

    const handleClick = (rowData) => {
      navigate(`${rowData[0]}`)
    }

<MUIDataTable 
    className={styles.innerTable}
    data={columnData} 
    columns={petColumns} 
    options={options}
    >
</MUIDataTable>

Where I'm running into trouble is that I want to turn each of the pet names that are being listed into a link that will navigate to the pets respective profile when clicked on. I will show some of my attempts to do this and explain what the results were.

ATTEMPT 1

Here I added pet.id to the petList object. This id is used with react-router-dom and Link to travel to the proper profile page for the pet. I then set up a customBodyRender (I tried both customBodyRender and customBodyRenderLite) which is supposed to map through the pet list and create a link with the pet name for each entry and then join together the new link divs.

I'm actually fairly stumped on this because I keep getting "Uncaught TypeError: value.map is not a function". Which makes me think that the value of property is not being recognized as an array. However, I found several StackOverFlow posts where value is treated properly as an array and can be joined together such as here.

I also recognize though that the array in that stack overflow is flat which mine is not but I even added the "enableNestedDataAccess: '.'," to that specific set of options (even though it's already in the general options for the table) just to make sure. I tried some variations of metadata as well but couldn't get that to stick either, I honestly think I'm probably incorporating it wrong.

CODE EXAMPLE - I didn't include the code that remained unchanged

setPetColumns = [
        {
              name: "id",
              label: "Id",
              options: {
                display: 'excluded',
               }
         },
         {
              name: "name",
              label: "Owner Name",
         },
         {
              name: "pet",
              label: "Pets",
              options: {
                customBodyRenderLite: (value) => {    ****ATTEMPTED customBodyRender TO RENDER THE LINK AND THEN JOIN ****
                  return (
                     <>
                        {value.map((e) => {
                           <Link to={`/pets/id=${e.id}`} >{e.name}</Link>
                         }).join(', ')}
                      </>
                   )
                }
              }
         },
]

useEffect(() => {
    const ownerData = data.map((owner) => {
            const petList = []
            owner.Pets.forEach((pet) => {
              petList.push({
                name: pet.name,
                id: pet.id                         ****ID IS ADDED FOR NAVIGATION PURPOSES****
              })
            })

            const petObject = {
              id: owner.id,
              name: `${owner.User.firstName} ${owner.User.lastName}`,
              pet: petList
            }
            return petObject
          })
   setColumnData(ownerData)
}, [data])

When I console log columnData I get exactly what I'm expecting DATA EXAMPLE

    0:
         id: "30c9"
         name: "Paige"
         property: Array(3)
                0:
                      id: "df43"
                      name: "Sally"
               1:
                      id: "dea4"
                      name: "Sage"
               2:
                      id: "ger7"
                      name: "Ember"

ATTEMPT 2

My attempt here was kind of a hail mary, I'd been working trying to get the custom render to work for a long while so I thought I'd go at it from a different direction. I trying to include Link within the petObject that initially worked in providing the plain joined names.

My result of this attempt was that my data table rendered but I got [ object Object ] within the pet Columns if I included return in the .map function or an empty string if there was no return. I honestly didn't really expect this to work but was at a loss of what else to try. When I consoled the columnData it was either [ object Object ] or ""

setPetColumns = [
        {
              name: "id",
              label: "Id",
              options: {
                display: 'excluded',
               }
         },
         {
              name: "name",
              label: "Owner Name",
         },
         {
              name: "pet",
              label: "Pets",
         },
]

useEffect(() => {
    const ownerData = data.map((owner) => {
            const petList = []
            owner.Pets.forEach((pet) => {
              petList.push({
                name: pet.name,
                id: pet.id                         ****ID IS ADDED FOR NAVIGATION PURPOSES****
              })
            })

            const petObject = {
              id: owner.id,
              name: `${owner.User.firstName} ${owner.User.lastName}`,
              *****TRIED TO INCLUDE LINK WITHIN OBJECT****
              pet: petList.map((p) => {
                   return <Link to=`/pet/id=${p.id} >p.name </Link> ****If return isn't included a blank string is returned****
                 }).join(', '), 
            }
            return petObject
          })
   setColumnData(ownerData)
}, [data])

RESULT

Owners Pet Names
Paige [object Object]
Tech Version
Material-UI 4.12.3
MUI-datatables 4.1.2
React 17.0.2
browser Google Chrome

POlsen-92 avatar Mar 16 '22 20:03 POlsen-92