I am not a react expert, and I am trying to integrate a gridjs component with react-beautiful-dnd library. The objective is to add drag and drop functionaly to a gridjs table.
I have create a react component, that looks like this:
import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
function ForwardGrid({userdata}, ref) {
const users = userdata.data;
return (<>
<div ref={ref}>
<DragDropContext>
<table>
<thead>
<tr>
<th>#</th>
<th>Username</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<Droppable droppableId="droppable-1">
{(provider) => (
<tbody
className="text-capitalize"
ref={provider.innerRef}
{...provider.droppableProps}
>
{users?.map((user, index) => (
<Draggable
key={user.name}
draggableId={user.name}
index={index}
>
{(provider) => (
<tr {...provider.draggableProps} ref={provider.innerRef}>
<td {...provider.dragHandleProps}> =</td>
<td>{user.name}</td>
<td>{user.age}</td>
<td>{user.gender}</td>
</tr>
)}
</Draggable>
))}
{/*{provider.placeholder}*/}
</tbody>
)}
</Droppable>
</table>
</DragDropContext>
</div>
</>)
}
const forwardTable = React.forwardRef(ForwardGrid);
export default forwardTable;
this component return a forwardref, which is used in a parent component.
const tableRef = useRef(null);
const wrapperRef = useRef(null);
const [users, setUsers] = useState(userdata.data);
useEffect(() => {
console.log(tableRef.current)
const grid = new Grid({
from: tableRef.current,
}).render(wrapperRef.current);
}, []);
return (
<>
<div className="App mt-4">
<ForwardGrid ref={tableRef} userdata={userdata}/>
<div ref={wrapperRef}/>
</div>
</>
);
However, once .render(wrapperRef.current) is fired, the table is rendered but the drag and drop doesn't work. I have inspected the html generated and I have noticed that it is not correct. It does not contain all the attributes that react-beautiful-dnd would generate normally (for instance data-rbd-draggable-id, data-rbd-draggable-context-id etc).The drag and drop functionalty works fine with a normal html table.
I would expect this html generated.
<tr data-rbd-draggable-context-id="0" data-rbd-draggable-id="Subham" style="">
<td tabindex="0" role="button" aria-describedby="rbd-hidden-text-0-hidden-text-0"
data-rbd-drag-handle-draggable-id="Subham" data-rbd-drag-handle-context-id="0" draggable="false"> =
</td>
<td>Subham</td>
<td>21</td>
<td>male</td>
</tr>
insted it returns this.
<tr class="gridjs-tr">
<td data-column-id="#" class="gridjs-td">=</td>
<td data-column-id="username" class="gridjs-td">Jeevan</td>
<td data-column-id="age" class="gridjs-td">21</td>
<td data-column-id="gender" class="gridjs-td">male</td>
</tr>
any idea how can I solve the problem?
Related
There are two table components in the code with same number of columns. Need to move row from One table to Another table using Drag and Drop.
Example:
Table 1:
<table className='table1'>
<thead>
<tr><th>Col 1</th><th>Col 2</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>2</td></tr>
<tr><th>Col 2</th><th>Col 2</th></tr>
<tr><th>Col 3</th><th>Col 2</th></tr>
</tbody>
</table>
Table 2:
<table className='table2'>
<thead>
<tr><th>Col 1</th><th>Col 2</th></tr>
</thead>
<tbody>
<tr><td>2</td><td>23/td></tr>
<tr><th>Col 4</th><th>Col 23</th></tr>
<tr><th>Col 5</th><th>Col 32</th></tr>
</tbody>
</table>
Table 1 and Table 2 need to be drag and droppable. Can anyone help regarding this?
Here is the code, I'm trying to fetch some data from JSON file. Tables are filled by the data, I can drag within the tables, when I drag and drop to another table, I can't move the row to another table, but both are Drag and Droppable. There are no errors shown, when doing drag and drop.
import "./styles.css";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import "bootstrap/dist/css/bootstrap.min.css";
import userdata from "./tempData.json";
import user2data from "./tempData_2.json";
import { useState } from "react";
export default function App() {
const [users, setUsers] = useState(userdata.data);
const [userrs, setUserrs] = useState(user2data.data);
const handleDragEnd = (e) => {
if (!e.destination) return;
let tempData = Array.from(users);
let [source_data] = tempData.splice(e.source.index, 1);
tempData.splice(e.destination.index, 0, source_data);
setUsers(tempData);
};
const handleDraggEnd = (e) => {
if (!e.destinationn) return;
let temppData = Array.from(userrs);
let [sourcee_data] = temppData.splice(e.source.index, 1);
temppData.splice(e.destinationn.index, 0, sourcee_data);
setUserrs(temppData);
};
return (
<div className="App mt-4">
<DragDropContext onDragEnd={handleDragEnd}>
<table className="table borderd">
<thead>
<tr>
<th />
<th>Username</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<Droppable droppableId="droppable-2">
{(provider) => (
<tbody
className="text-capitalize"
ref={provider.innerRef}
{...provider.droppableProps}
>
{users?.map((user, index) => (
<Draggable
key={user.name}
draggableId={user.name}
index={index}
>
{(provider) => (
<tr {...provider.draggableProps} ref={provider.innerRef}>
<td {...provider.dragHandleProps}> = </td>
<td>{user.name}</td>
<td>{user.age}</td>
<td>{user.gender}</td>
</tr>
)}
</Draggable>
))}
{provider.placeholder}
</tbody>
)}
</Droppable>
</table>
</DragDropContext>
<DragDropContext onDragEnd={handleDraggEnd}>
<table className="table borderd">
<thead>
<tr>
<th />
<th>Username</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<Droppable droppableId="droppable-1">
{(provider) => (
<tbody
className="text-capitalize"
ref={provider.innerRef}
{...provider.droppableProps}
>
{userrs.map((user, index) => (
<Draggable
key={user.name}
draggableId={user.name}
index={index}
>
{(provider) => (
<tr {...provider.draggableProps} ref={provider.innerRef}>
<td {...provider.dragHandleProps}> = </td>
<td>{user.name}</td>
<td>{user.age}</td>
<td>{user.gender}</td>
</tr>
)}
</Draggable>
))}
{provider.placeholder}
</tbody>
)}
</Droppable>
</table>
</DragDropContext>
</div>
);
}
This may already have an answer but I have not been able to find one that doesn't suggest a work around that doesn't make use of ref. That said, using ref may not be the preferred approach.
I have a table where each row has a popover menu as the last column. Currently, the ref will always point to the last mapped item (last row's menu).
How can I make sure each row's menu has its own reference?
const [showMenu, setShowMenu] = useState(false);
const menu = useRef(null);
return (
<table>
<caption>Table</caption>
<thead>
<tr>
<th>Col 1</th>
<th>Col 2</th>
<th>Col 3</th>
<th>Col 4</th>
</tr>
</thead>
<tbody>
{data.map((item, index) =>
<tr key={ index }>
<td>{ item.a }</td>
<td>{ item.b}</td>
<td>{ item.c }</td>
<td>
<div className="popover-group">
<button
className="popover-menu-button"
onClick={ () => setShowMenu(!showMenu) }
>Show Menu</button>
<div className="popover-menu" ref={ menu }>
/* menu content */
</div>
</div>
</td>
</tr>
)}
</tbody>
</table>
)
I should add that I will need to be able to access the specific menu elsewhere like here
useEffect(() => {
if (showMenu){
menu.current.style.setProperty('opacity', '1')
menu.current.style.setProperty('display', 'block')
} else {
menu.current.style.setProperty('opacity', '0')
menu.current.style.setProperty('display', 'none')
}
}, [showMenu])
So I will need to access menu.current[/*some index*/]. How could I accomplish this?
I have a question about the component panel in react
Currently, I have a requirement to create a common component for the Order and Product tables. But the two tables have different numbers of columns, one side has many columns and one side has few columns, besides there are also different table names.
I have a piece of code like this
import React from 'react';
import {Table, Image} from 'react-bootstrap';
import '../Table/index.css';
import Button from '../Button/index';
const TableItem = ({productList}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
<th>No. </th>
<th>Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{productList.map((product, index) => (
<tr key={index}>
<td>{product.id}</td>
<td><Image src={product.image} /></td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>
<Button variant="success" onClick={redirectToEdit}>Edit</Button>
<Button variant="danger" onClick={deleteProductItem}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
);
}
export default TableItem;
That code I have created for the product table, but the order table is not because I do not know how to do it properly. I named fixed for the column, I know this is wrong because the order table will also retrieve components from this table, so I can not make the name like that
The order table also has the following columns: Username, address, quantity, status .....
How can I change the code in this component that can be used for both tables
Can anyone help me to explain for this, thank you so much
So you can do something like this, create a table component and pass the columns list and data in there as props that way you can control the table from the parents component and can be used in any way as you want.
const TableItem = ({data, columns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{
columns.map((column, index) => {
<th key={ index }>{ column.name }</th>
}
}
</tr>
</thead>
<tbody>
{data.map((product, index) => (
<tr key={index}>
<td>{product.id}</td>
<td><Image src={product.image} /></td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>
<Button variant="success" onClick={redirectToEdit}>Edit</Button>
<Button variant="danger" onClick={deleteProductItem}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
);
}
and in the parent component you can do something like
const parentComponent = () => {
return {
<TableItem columns={ productColumns } data={ productData } />
<TableItem columns={ orderColumns } data={ orderData }
}
}
Note Code not tested and is pseudo
You can pass columns (Array) as a props and render it.
const columns = ['username', 'quantity', 'etc'];
const TableItem = ({List, columns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{columns.map((name, index) => (
<th key={index}>{name}</th>
))}
</tr>
</thead>
<tbody>
</Table>
)
})
If you have some common columns then you can pass one more props commonColumns like that.
const TableItem = ({List, columns, commonColumns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{commonColumns.map((name, index) => (
<th key={index}>{name}</th>
))}
{columns.map((name, index) => (
<th key={index}>{name}</th>
))}
</tr>
</thead>
<tbody>
</Table>
)
})
Hope this will help you.
I am working on a react project I am trying to display data in td, the data is coming from backend and that data is coming in console. But I am not able to display data in table.
For this project I am using functional Component
This is my Child.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function Child() {
const [oldEmployData, newEmployData] = useState([])
async function getEmploysData() {
try {
const response = await axios.get('http://localhost:1000/api/employList');
newEmployData(response.data);
console.log(response.data)
} catch(error) {
console.log(error)
}
}
useEffect(() => {
getEmploysData()
}, [])
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<table className="table table-bordered">
<thead>
<tr>
<th>Employ Name</th>
<th>Employ Age</th>
<th>Employ Stream</th>
<th>Employ Address</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<button className='btn btn-primary'>Edit</button>
</td>
<td>
<button className='btn btn-danger'>Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
)
}
If I am clear please put a comment
Assuming your axios request is responding with a valid JSON array, you can render that data directly into your table via Array#map() by extending your render logic as shown below:
<tbody>
{data.map(item =>
<tr key={item.id}>
{/* assuming each item in JSON array has firstName, lastName, email
and password fields */}
<td>{item.firstName}</td>
<td>{item.lastName}</td>
<td>{item.email}</td>
<td>{item.password}</td>
{/* suppose you have editItem() and deleteItem() defined, these
can be invoked (during click event) for each item like this */}
<td>
<button className="btn btn-primary"
onClick={() => editItem(item)}>Edit</button>
</td>
<td>
<button className="btn btn-danger"
onClick={() => deleteItem(item)}>Delete</button>
</td>
</tr>)}
</tbody>
An important point to note is that each <tr> element being mapped must have a key prop supplied with a value that is unique to the item being rendered/mapped. With that in mind, the assumption being made above is that each item in the data array has a unique id.
Suppose json data like this
let data = [
{id:1,name:'One'},
{id:2,name:'Two'},
{id:3,name:'Three'}
]
Iterate like this
{data.map(d=>
<tr key={d.id}>
<td>{d.name}</td>
<td>{}</td>
<td>{}</td>
<td></td>
<td><button className='btn btn-primary' onClick={()=>handleEdit(d.id)>Edit</button></td>
<td><button className='btn btn-danger' onClick={()=>handleDelete(d.id)}>Delete</button></td>
</tr>
)}
I could render the herader's table users through for exemple: {user[name]}. But I couldn't render the header itself who is the [name] for example rendering: name of Leanne Graham. Can anyone help me with this?
import React from 'react'
const Users= ({ users}) => {
return (
<div>
{users.map((user,index) => (
<div key={index}>
<div className="container smcontainer d-flex justify-content-start">
<div className="row">
<div className="col-md-12">
<table className="table table-striped">
<thead>
</thead>
<tbody>
<tr>
<td className=""> {user['id']} </td>
<td className=""> {user['name']} </td>
<td className=""> {user['username']} </td>
<td className=""> {user['email']} </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
))}
</div>
)}
export default Products
This is the App component
class App extends Component {
constructor(props){
super(props)
this.state= {
users: [],
}
}
async componentDidMount() {
const url = ('https://jsonplaceholder.typicode.com/users')
const response = await fetch (url)
const data = await response.json()
this.setState({users: data.itemsList})
console.log({users: data.itemsList})
}
render() {
return (
<Users users = {this.state.users} />
)
}
}
export default App;
When you map over an array, whatever you return is returned each time for every element in the array. This code will create a whole new table for each user.
I think what you want to do is define your column headers separately from your map call and then map over the array to generate the rows:
const columns = ["ID", "Name", "Username", "Email"];
...
<div>
<div className="container smcontainer d-flex justify-content-start">
<div className="row">
<div className="col-md-12">
<table className="table table-striped">
<thead>
{columns.map(c => <th>{c}</th>)}
</thead>
<tbody>
{users.map((user, index) => (
<tr>
<td className=""> {user['id']} </td>
<td className=""> {user['name']} </td>
<td className=""> {user['username']} </td>
<td className=""> {user['email']} </td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
I made a Codepen demonstrating both ways, but to me the first one makes much more sense.
That should give you what you want.
<thead>
{user['name']}
<thead/>
If you want to take the first name of Leanne Graham, you can do:
<thead>
{user['name'].split(" ")[0]}
<thead/>