Is it possible to make a table with nested columns using material-table library?
Final result that I want to achieve
Yes its possible with material-table . You have to use Components property to achieve that.
function App() {
const columns = [...];
const data = [...];
return (
<div className="App">
<MaterialTable
columns={columns}
data={data}
components={{
Header: props => {
return (
<TableHead>
<TableRow>
<TableCell colSpan={2} align="center">
Average A
</TableCell>
<TableCell colSpan={2} align="center">
Average B
</TableCell>
</TableRow>
<TableRow>
<TableCell align="center">Lower</TableCell>
<TableCell align="center">Upper</TableCell>
<TableCell align="center">Lower</TableCell>
<TableCell align="center">Upper</TableCell>
</TableRow>
</TableHead>
);
},
Row: ({ data }) => {
return (
<TableRow>
<TableCell align="center">{data.lowerA}</TableCell>
<TableCell align="center">{data.upperA}</TableCell>
<TableCell align="center">{data.lowerB}</TableCell>
<TableCell align="center">{data.upperB}</TableCell>
</TableRow>
);
}
}}
/>
</div>
);
}
DEMO: Codesandbox link
You can use colspan. Check example here.
https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_td_colspan
Related
How do I input conditional if else statement in a TableCell for ReactJS. I want the data table to output nothing is found if the search box returns nothing.
Here is the code:
export default function DataTable () {
const [query, setQuery] = useState('')
const [tableData, setTableData] = useState([])
useEffect( function () {
const fetchUsers = async function () {
const response = await axios.get(`/search/?query=${query}`)
setTableData(response.data)
}
if (query.length === 0 || query.length > 2) fetchUsers()
}, [query])
return (
<>
<input type={'text'} placeholder={'Enter Teacher ID Number....'} className='search-bar' onChange={(event) => setQuery(event.target.value)} />
<TableContainer component={Paper} className='data-table'>
<Table sx={{minWidth: 650}} aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell className='tableCell'>ID</TableCell>
<TableCell className='tableCell'>Name</TableCell>
<TableCell className='tableCell'>Surname</TableCell>
<TableCell className='tableCell'>Title</TableCell>
<TableCell className='tableCell'>Email</TableCell>
<TableCell className='tableCell'>Action #1</TableCell>
<TableCell className='tableCell'>Action #2</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ tableData.map((data) => (
<TableRow key={data.id} sx={{ '&:last-child td, &:last-child th': {border:0}}}>
<TableCell className='tableCell'> { data.id }</TableCell>
<TableCell className='tableCell'> { data.name }</TableCell>
<TableCell className='tableCell'> { data.surname }</TableCell>
<TableCell className='tableCell'> { data.title }</TableCell>
<TableCell className='tableCell'> { data.email}</TableCell>
<TableCell className='tableCell'>
<button className={'verify'}>
<Link to={`/search/${data.id}`} className={'verify'} state={data}>Verify</Link>
</button>
</TableCell>
<TableCell className='tableCell'>
<button className={`drop`}>
<Link to={`/drop/${data.id}`} className={'drop'} state={data}>Drop</Link>
</button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}
I tried to input ternary operator '?' but it returns the first result
First of all I recommend to you separate the mapped Row in a separated component to handle easily and get the code more readable. This component could be in other file.
On the other hand I highly recommend if you are consuming the data from rest service, is using React Query.
export default function DataTable () {
const [query, setQuery] = useState('')
const [tableData, setTableData] = useState([])
useEffect( function () {
const fetchUsers = async function () {
const response = await axios.get(`/search/?query=${query}`)
setTableData(response.data)
}
if (query.length === 0 || query.length > 2) fetchUsers()
}, [query])
function UserTableRow(user){
return (
<TableRow key={user.id} sx={{ '&:last-child td, &:last-child th': {border:0}}}>
<TableCell className='tableCell'> { user.id }</TableCell>
<TableCell className='tableCell'> { user.name }</TableCell>
<TableCell className='tableCell'> { user.surname }</TableCell>
<TableCell className='tableCell'> { user.title }</TableCell>
<TableCell className='tableCell'> { user.email}</TableCell>
<TableCell className='tableCell'>
<button className={'verify'}>
<Link to={`/search/${user.id}`} className={'verify'} state={data}>Verify</Link>
</button>
</TableCell>
<TableCell className='tableCell'>
<button className={`drop`}>
<Link to={`/drop/${user.id}`} className={'drop'} state={data}>Drop</Link>
</button>
</TableCell>
</TableRow>
)
}
return (
<>
<input type={'text'} placeholder={'Enter Teacher ID Number....'} className='search-bar' onChange={(event) => setQuery(event.target.value)} />
<TableContainer component={Paper} className='data-table'>
<Table sx={{minWidth: 650}} aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell className='tableCell'>ID</TableCell>
<TableCell className='tableCell'>Name</TableCell>
<TableCell className='tableCell'>Surname</TableCell>
<TableCell className='tableCell'>Title</TableCell>
<TableCell className='tableCell'>Email</TableCell>
<TableCell className='tableCell'>Action #1</TableCell>
<TableCell className='tableCell'>Action #2</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ tableData?.map((user) => <UserTableRow user={user}/>)}
{!tableData.length && <div>No results found.</div>}
</TableBody>
</Table>
</TableContainer>
</>
)
}
I have this bit of code where I'm getting the warning:
Each child in a list should have a unique "key" prop. Check the render
method of AllPersonnel... at TableRow
And the table cells are not rendering. Any help would be appreciated. Thanks!
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow key={uuidv4()}>
{cols.map((col) => {
return <TableCell key={col.path}>{col.name}</TableCell>
})}
</TableRow>
</TableHead>
<TableBody>
{personnel.map((row, i) => {
return (
<TableRow key={row.id}>
<TableCell key={uuidv4()}>{row.first_name}</TableCell>
<TableCell key={uuidv4()}>{row.last_name}</TableCell>
<TableCell key={uuidv4()}>{row.section}</TableCell>
<TableCell key={uuidv4()}>{row.role}</TableCell>
<TableCell key={uuidv4()}>{row.employee_type}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
You don't need a key on your TableRow, only on the first child in the return of an iterator.
Try :
{personnel.map((row, i) => {
const key = uuidv4()
return (
<TableRow key={key}>
<TableCell>{row.first_name}</TableCell>
...
Normaly...
I'm using React, typescript and Material UI. I've created a pretty common "Create user form" with basic inputs for username, name, email, ect. Added two buttons, "Edit" and "Delete." Everything seems to function properly, however, I cannot get this error message resolved.
Warning: validateDOMNesting(...): <td> cannot appear as a child of <div>.
Here's the table from a react component:
<TableContainer className={classes.scroll} component={Paper}>
<Table stickyHeader aria-label="table">
<TableHead>
<TableRow>
<TableCell>Username</TableCell>
<TableCell align="right">First name</TableCell>
<TableCell align="right">Last name</TableCell>
<TableCell align="right">Email</TableCell>
<TableCell align="right">Connect_username</TableCell>
<TableCell align="right">Role</TableCell>
<TableCell align="left">Edit</TableCell>
<TableCell align="left">Delete</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(rowsPerPage > 0
? props?.items.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: props?.items
).map(item => (
<TableRow key={item.user_id}>
<TableCell component="th">{item.username}</TableCell>
<TableCell align="right">{item.first_name}</TableCell>
<TableCell align="right">{item.last_name}</TableCell>
<TableCell align="right">{item.email}</TableCell>
<TableCell align="right">{item.connect_username}</TableCell>
<TableCell align="right">{item.role?.map(r=>r.role).join(",")}</TableCell>
<TableCell>
<Button onClick={() => props.handleEdit(item)}>Edit</Button>
</TableCell>
<TableCell>
<Button onClick={() => props.handleConfirmDelete(item.user_id)}>Delete</Button>
</TableCell>
</TableRow>
))}
</TableBody>
<TablePagination
rowsPerPageOptions={[10, 25, { label: 'All', value: -1 }]}
count={props.items.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Table>
</TableContainer>
component={Paper} is likely causing this. Can you try removing it? If you want the table to appear on a Paper component, then try nesting TableContainer under Paper.
put TablePagination in the TableBody tag and wrap it in TableRow.
Had a similar issue and this fixed it.
Like this:
<TableBody>
{(rowsPerPage > 0
? props?.items.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: props?.items)
.map(item => (
<TableRow key={item.user_id}>
<TableCell component="th">{item.username}</TableCell>
<TableCell align="right">{item.first_name}</TableCell>
<TableCell align="right">{item.last_name}</TableCell>
<TableCell align="right">{item.email}</TableCell>
<TableCell align="right">{item.connect_username}</TableCell>
<TableCell align="right">{item.role?.map(r=>r.role).join(",")}</TableCell>
<TableCell>
<Button onClick={() => props.handleEdit(item)}>
Edit
</Button>
</TableCell>
<TableCell>
<Button onClick={() => props.handleConfirmDelete(item.user_id)}>
Delete
</Button>
</TableCell>
</TableRow>
<TableRow>
<TablePagination
rowsPerPageOptions={[10, 25, { label: 'All', value: -1 }]}
count={props.items.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</TableRow>
))}
</TableBody>
update: "#mui/material": "^5.10.9"
in my case this error was causing TablePagination and not Paper component.
working solution is wrapping pagination not only with row but also footer as follow
<TableContainer component={Paper}>
<Table>
<TableBody>
...
</TableBody>
<TableFooter>
<TableRow>
<TablePagination />
</TableRow>
</TableFooter>
</Table>
</TableContainer>
mui docs: https://mui.com/material-ui/react-table/#custom-pagination-actions
currently I am trying to display countries into a Material-UI table. However, all the data is displaying into one row. Anyone know how to fix it? The country array contains elements per index.
<TableCell component="th" id={labelId} scope="row" >
{country}
</TableCell>
Try this
<Table >
<TableBody>
{generateRows()}
</TableBody>
</Table>
const countries =['india','usa']
const generateRows = (countries)=>{
return countries.map(country=>{
return (
<TableRow>
<TableCell component="th" scope="row">
{country}
</TableCell>
</TableRow >
)
})
}
I think this is an easy problem, but I am struggling to understand how to do this since I don't have a class. I am trying to make this table sortable. I want to be able to sort based on the column that is clicked on.
Do I need to add a class? I am very new to ReactJS. Thanks for your help!
function CreateGradeTable(props) {
return (
<div>
<Table>
<TableHead>
<TableRow>
<TableCell className={props.tHead}>Student</TableCell>
<TableCell className={props.tHead}>Course</TableCell>
<TableCell className={props.tHead}>Term</TableCell>
<TableCell className={props.tHead}>Grade</TableCell>
<TableCell className={props.tHead}>Final</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.data.map((gradeDetail, i) => {
const {
gradeId,
courseName,
courseId,
studentName,
studentId,
courseTerm,
grade,
finalGradeForTerm,
} = gradeDetail;
return (
<TableRow
key={gradeId}
className={`${props.tRow} ${i % 2 !== 0 ? props.striped : ''}`}
>
<TableCell>
<Link to={`/student/${studentId}`}>
<StyledLink>{studentName}</StyledLink>
</Link>
</TableCell>
<TableCell>
<Link to={`/course/${courseId}`}>
<StyledLink>{courseName}</StyledLink>
</Link>
</TableCell>
<TableCell align="left">{courseTerm}</TableCell>
<TableCell align="left">{grade}</TableCell>
<TableCell align="left">{finalGradeForTerm}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
);
}
I see that you are using Material-UI to display the table.
You need to use 'TableSortLabel' to display Header. This will also show ascending/descending arrows.
Then write an onClick handler on TableSortLabel to change state thus triggering re-render.
See the working example here
https://codesandbox.io/s/condescending-beaver-q0owe?fontsize=14
<TableBody>
{
props.data.sort(order === 'asc'? ascCompare: desCompare ).map((gradeDetail, i) => {
const {
grade,
courseName,
studentName,
} = gradeDetail;
return (
<TableRow
key={i}
>
<TableCell align="left">{grade}</TableCell>
<TableCell align="left">{studentName}</TableCell>
<TableCell align="left">{courseName}</TableCell>
</TableRow>
);
})}
</TableBody>