Related
i'm new on react(hooks) typescript, trying to learn by doing,
here i have created antd table(which works), on the right side of table is 'Edit' it is clickable and works well, but my question is how to make each row clickable instead of that 'Edit' ? like i could click anywhere on the row and it should take me to its 'Edit' link instead of me clicking just on 'Edit':
import { useTranslation } from "react-i18next";
const { t } = useTranslation();
const columns = [
{
title: t("tilaus.state"),
dataIndex: "state",
key: "state",
render: (value: OrderState) => (
<span className="material-icons">
</span>
),
},
{
title: t("request.parcelId"),
dataIndex: "parcelId",
key: "parcelId",
},
{
title: t("request.date"),
dataIndex: "date",
key: "date",
render: (value: Date) => <div>{value.toLocaleDateString("af-AF")}</div>,
},
{
title: t("request.sender"),
dataIndex: "sender",
key: "sender",
render: (value: CustomerDto) => <div>{value.name}</div>,
},
{
title: t("request.recipient"),
dataIndex: "recipient",
key: "recipient",
render: (value: CustomerDto) => <div>{value.name}</div>,
},
{
title: t("request.price"),
dataIndex: "price",
key: "price",
},
{
title: "",
dataIndex: "id",
key: "id",
render: (value: string) => (
<Link to={"details/" + value}>{t("request.edit")}</Link>
),
},
];
<Table
dataSource={orders}
columns={columns}
pagination={false}
scroll={{ x: true }}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleClick(record);
},
};
}}
/>
Looks like you are using ant.d table. I've grabbed one of the examples and console logged the output. You can find it here: https://codesandbox.io/s/s1xds?file=/index.js to show you how the onclick in the entire row is being triggered.
For your specific thing, you need to change onRow prop. You can't add a Link directly to the row, but you can use history from react-router (if you are using it, docs here) or directly mutate the URL when onclick is called.
So in your case, you'll have to do this:
<Table
dataSource={orders}
columns={columns}
pagination={false}
scroll={{ x: true }}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
history.push(`/details/${record.id}`);
},
};
}}
/>
or
<Table
dataSource={orders}
columns={columns}
pagination={false}
scroll={{ x: true }}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
window.location.href = `${window.location.href}/details/${record.id}`
},
};
}}
/>
I'm trying to achieve adding edit and delete button via MUI Datatable it's already applied but whenever I pressed the edit button the URL says the undefined. Here is an image and my code. Thanks for your help really appreciate it.
as you can see it says /client/edit/undefined whereas should be an id
Code:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import MUIDataTable from "mui-datatables";
const Client = (props) => (
<>
<Link to={"client/edit/" + props.client._id} className="btn btn-primary">
Edit
</Link>
<a
href="client"
onClick={() => {
props.deleteClient(props.client._id);
}}
className="btn btn-danger"
>
Delete
</a>
</>
);
export default class ClientsList extends Component {
constructor(props) {
super(props);
this.deleteClient = this.deleteClient.bind(this);
this.state = { clients: [] };
}
componentDidMount() {
axios
.get("http://localhost:5000/clients/")
.then((response) => {
this.setState({ clients: response.data });
})
.catch((error) => {
console.log(error);
});
}
deleteClient(id) {
axios.delete("http://localhost:5000/clients/" + id).then((response) => {
console.log(response.data);
});
this.setState({
clients: this.state.clients.filter((el) => el._id !== id),
});
}
clientList(currentclient) {
return (
<Client
client={currentclient}
deleteClient={this.deleteClient}
key={currentclient[0]}
/>
);
}
render() {
const columns = [
{
name: "_id",
options: {
display: false,
},
},
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "address",
label: "Address",
options: {
filter: true,
sort: true,
},
},
{
name: "mobile",
label: "Mobile",
options: {
filter: true,
sort: true,
},
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
},
},
{
name: "gender",
label: "Gender",
options: {
filter: true,
sort: true,
},
},
{
name: "birthday",
label: "Birthday",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookPage",
label: "Facebook Page",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookName",
label: "Facebook Name",
options: {
filter: true,
sort: true,
},
},
{
name: "existing",
label: "Existing",
options: {
filter: true,
sort: true,
},
},
{
name: "remarks",
label: "Remarks",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
return <>{this.clientList(tableMeta.rowData)}</>;
},
},
},
];
const { clients } = this.state;
return (
<>
<br />
<br />
<br />
<div style={{ margin: "10px 15px", overflowX: "auto" }}>
<Link to={"client/create/"} className="btn btn-primary pull-right">
Add Client Data
</Link>
<br />
<br />
<br />
<MUIDataTable data={clients} columns={columns} />
</div>
</>
);
}
}
Everyone, the Table data is transposed into an array, this is my code if you wanted to have a edit and delete button using mui datatables.
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import MUIDataTable from "mui-datatables";
const Client = (props) => (
<>
{console.log("Client Props", props)}
<Link to={"client/edit/" + props.client[0]} className="btn btn-primary">
Edit
</Link>
<a
href="client"
onClick={() => {
props.deleteClient(props.client[0]);
}}
className="btn btn-danger"
>
Delete
</a>
</>
);
export default class ClientsList extends Component {
constructor(props) {
super(props);
this.deleteClient = this.deleteClient.bind(this);
this.state = { clients: [] };
}
componentDidMount() {
axios
.get("http://localhost:5000/clients/")
.then((response) => {
this.setState({ clients: response.data });
})
.catch((error) => {
console.log(error);
});
}
deleteClient(id) {
axios.delete("http://localhost:5000/clients/" + id).then((response) => {
console.log(response.data);
});
this.setState({
clients: this.state.clients.filter((el) => el._id !== id),
});
}
clientList(currentclient) {
return (
<Client
client={currentclient}
deleteClient={this.deleteClient}
key={currentclient[0]}
/>
);
}
render() {
const columns = [
{
name: "_id",
options: {
display: false,
},
},
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "address",
label: "Address",
options: {
filter: true,
sort: true,
},
},
{
name: "mobile",
label: "Mobile",
options: {
filter: true,
sort: true,
},
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
},
},
{
name: "gender",
label: "Gender",
options: {
filter: true,
sort: true,
},
},
{
name: "birthday",
label: "Birthday",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookPage",
label: "Facebook Page",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookName",
label: "Facebook Name",
options: {
filter: true,
sort: true,
},
},
{
name: "existing",
label: "Existing",
options: {
filter: true,
sort: true,
},
},
{
name: "remarks",
label: "Remarks",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
return <>{this.clientList(tableMeta.rowData)}</>;
},
},
},
];
const { clients } = this.state;
return (
<>
<br />
<br />
<br />
<div style={{ margin: "10px 15px", overflowX: "auto" }}>
<Link to={"client/create/"} className="btn btn-primary pull-right">
Add Client Data
</Link>
<br />
<br />
<br />
<MUIDataTable data={clients} columns={columns} />
</div>
</>
);
}
}
Hi Everyone, I'm trying to achieve adding edit and delete button in ReactJS using Mui Datatable, but the problem is that it keeps on repeating because of the Map sorry I'm just new in ReactJS anyways, here is my image and my code:
This is an example of my image:
And This My Code:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import MUIDataTable from "mui-datatables";
const Client = (props) => (
<>
<Link to={"client/edit/" + props.client._id} className="btn btn-primary">
Edit
</Link>
<a
href="client"
onClick={() => {
props.deleteClient(props.client._id);
}}
className="btn btn-danger"
>
Delete
</a>
</>
);
export default class ClientsList extends Component {
constructor(props) {
super(props);
this.deleteClient = this.deleteClient.bind(this);
this.state = { clients: [] };
}
componentDidMount() {
axios
.get("http://localhost:5000/clients/")
.then((response) => {
this.setState({ clients: response.data });
})
.catch((error) => {
console.log(error);
});
}
deleteClient(id) {
axios.delete("http://localhost:5000/clients/" + id).then((response) => {
console.log(response.data);
});
this.setState({
clients: this.state.clients.filter((el) => el._id !== id),
});
}
// This is the map I was talking about:
clientList() {
return this.state.clients.map((currentclient) => {
return (
<Client
client={currentclient}
deleteClient={this.deleteClient}
key={currentclient._id}
/>
);
});
}
render() {
const columns = [
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "address",
label: "Address",
options: {
filter: true,
sort: true,
},
},
{
name: "mobile",
label: "Mobile",
options: {
filter: true,
sort: true,
},
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
},
},
{
name: "gender",
label: "Gender",
options: {
filter: true,
sort: true,
},
},
{
name: "birthday",
label: "Birthday",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookPage",
label: "Facebook Page",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookName",
label: "Facebook Name",
options: {
filter: true,
sort: true,
},
},
{
name: "existing",
label: "Existing",
options: {
filter: true,
sort: true,
},
},
{
name: "remarks",
label: "Remarks",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: () => {
return <>{this.clientList()}</>;
},
},
},
];
const { clients } = this.state;
return (
<>
<br />
<br />
<br />
<div style={{ margin: "10px 15px", overflowX: "auto" }}>
<Link to={"client/create/"} className="btn btn-primary pull-right">
Add Client Data
</Link>
<br />
<br />
<br />
<MUIDataTable data={clients} columns={columns} />
</div>
</>
);
}
}
Thank you for your help and understanding I really appreciate it!
You can just not use map in the clientList() function because you are returning (edit, delete ) of all the clients for each row in the table. you also can pass the row data like I will show in the link on each button and have the _id as a hidden column on your table so that you can have access on it.
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import MUIDataTable from "mui-datatables";
const Client = (props) => (
<>
<Link to={"client/edit/" + props.client._id} className="btn btn-primary">
Edit
</Link>
<a
href="client"
onClick={() => {
props.deleteClient(props.client._id);
}}
className="btn btn-danger"
>
Delete
</a>
</>
);
export default class ClientsList extends Component {
constructor(props) {
super(props);
this.deleteClient = this.deleteClient.bind(this);
this.state = {
clients: [{
}]
};
}
componentDidMount() {
axios
.get("http://localhost:5000/clients/")
.then((response) => {
this.setState({ clients: response.data });
})
.catch((error) => {
console.log(error);
});
}
deleteClient(id) {
axios.delete("http://localhost:5000/clients/" + id).then((response) => {
console.log(response.data);
});
this.setState({
clients: this.state.clients.filter((el) => el._id !== id),
});
}
// This is the map I was talking about:
clientList(currentclient) {
// current cleint her is an array that contain all the columns values for the row specify
// assuming that _id will be the first column
return (
<Client
client={currentclient}
deleteClient={this.deleteClient}
key={currentclient[0]}
/>
);
}
render() {
const columns = [
{
name: "_id",
options: {
display: false,
}
},
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "address",
label: "Address",
options: {
filter: true,
sort: true,
},
},
{
name: "mobile",
label: "Mobile",
options: {
filter: true,
sort: true,
},
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
},
},
{
name: "gender",
label: "Gender",
options: {
filter: true,
sort: true,
},
},
{
name: "birthday",
label: "Birthday",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookPage",
label: "Facebook Page",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookName",
label: "Facebook Name",
options: {
filter: true,
sort: true,
},
},
{
name: "existing",
label: "Existing",
options: {
filter: true,
sort: true,
},
},
{
name: "remarks",
label: "Remarks",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
return <>{this.clientList(tableMeta.rowData)}</>;
},
},
},
];
const { clients } = this.state;
return (
<>
<br />
<br />
<br />
<div style={{ margin: "10px 15px", overflowX: "auto" }}>
<Link to={"client/create/"} className="btn btn-primary pull-right">
Add Client Data
</Link>
<br />
<br />
<br />
<MUIDataTable data={clients} columns={columns} />
</div>
</>
);
}
}
you can deconstruct clients data from state and then pass it to MUIDataTable
const { clients } = this.state;
const rows = clients.map((client) => {
return {
// assuming atributes
name: client.name,
address: client.address,
mobile: client.mobile,
email: client.email,
gender: client.gender,
birthday: client.birthday,
action: <Link to=`client/edit/${client.id}` calssName='btn btn-primary'> Edit </Link> <a href='client' onClick={() => this.deleteClient(client.id)}> delete </a>
}
}
and then pass it in data props in MUIDataTable
<MUIDataTable data={rows} columns={columns} />
this is an example of a working snippet, tweak it to match you need
const rows = orders.map((order) => {
return {
ref: <Link to={"/orders/" + order.ref}>{order.ref.slice(0, 8)}</Link>,
amount: order.amount,
donated: order.ticketsDetails[0].ticketDonate != "" ? "Yes" : "No",
date: order.createdAt.slice(0, 16),
};
});
const columns = [
{
label: "Ref",
name: "ref",
options: {
filter: true,
sort: true,
},
},
{
label: "Amount",
name: "amount",
options: {
filter: true,
sort: true,
},
},
{
label: "Date",
name: "date",
options: {
filter: true,
sort: true,
},
},
{
label: "Donated",
name: "donated",
options: {
filter: true,
sort: true,
},
},
];
return (
<div className="orders-container">
<MUIDataTable columns={columns} data={rows} />
</div>
);
I'm new on ReactJS and this is my first page that I created, but I'm having some problems with set variables.
What I need is fill the variable table.data with the values that comes from const response = await api.get('/users') and render the table with this values when page loads.
I have the following code:
import React, { useState, useEffect } from 'react';
import { Fade } from "#material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '#material-ui/core/styles';
import api from '../../services/api.js';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: '70%',
margin: 'auto',
marginTop: 20,
boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
}
}));
function User(props) {
const classes = useStyles();
const [checked, setChecked] = useState(false);
let table = {
data: [
{ name: "Patrick Mahomes", sector: "Quaterback", email: "patrick#nfl.com", tel: "1234" },
{ name: "Tom Brady", sector: "Quaterback", email: "tom#nfl.com", tel: "5678" },
{ name: "Julio Jones", sector: "Wide Receiver", email: "julio#nfl.com", tel: "9876" }
]
}
let config = {
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Sector', field: 'sector' },
{ title: 'E-mail', field: 'email'},
{ title: 'Tel', field: 'tel'}
],
actions: [
{ icon: 'create', tooltip: 'Edit', onClick: (rowData) => alert('Edit')},
{ icon: 'lock', tooltip: 'Block', onClick: (rowData) => alert('Block')},
{ icon: 'delete', tooltip: 'Delete', onClick: (rowData) => alert('Delete')},
{ icon: 'visibility', tooltip: 'Access', onClick: (rowData) => alert('Access')},
{ icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
],
options: {
headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
actionsColumnIndex: -1,
exportButton: true,
paging: true,
pageSize: 10,
pageSizeOptions: [],
paginationType: 'normal'
},
localization: {
body: {
emptyDataSourceMessage: 'No data'
},
toolbar: {
searchTooltip: 'Search',
searchPlaceholder: 'Search',
exportTitle: 'Export'
},
pagination: {
labelRowsSelect: 'Lines',
labelDisplayedRows: '{from} to {to} for {count} itens',
firstTooltip: 'First',
previousTooltip: 'Previous',
nextTooltip: 'Next',
lastTooltip: 'Last'
},
header: {
actions: 'Actions'
}
}
}
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
table.data = response.data;
}
loadUsers();
}, [])
return (
<>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
<div className={classes.root}>
<MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={table.data} actions={config.actions}></MaterialTable>
</div>
</Fade>
</>
);
}
export default User;
The previous example will show 3 users that I fixed on variable table.data with 4 columns (name, sector, email, tel).
In a functional component, each render is really a new function call. So any variables you declare inside the component and destroyed and re-created. This means that table is set back to your initial value each render. Even if your useEffect is setting it correctly after the first render, it will just be reset on the next.
This is what state is for: to keep track of variables between renders. Replace your let table, with a new state hook.
const [table, setTable] = useState({
data: [
{ name: "Patrick Mahomes", sector: "Quaterback", email: "patrick#nfl.com", tel: "1234" },
{ name: "Tom Brady", sector: "Quaterback", email: "tom#nfl.com", tel: "5678" },
{ name: "Julio Jones", sector: "Wide Receiver", email: "julio#nfl.com", tel: "9876" }
]
});
Then use it like this:
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
setTable(prev => ({...prev, data: response.data});
}
loadUsers();
}, [])
Since table.data is not a state variable, it is regenerated as it was declared originally every time the component renders, meaning that by the time it arrives as a prop to your component it will always be the same value (when you change the value of table.data in useEffect it is too late). You need to change table.data to a state variable, and then in your useEffect hook you can update the value of table.data to the value of response.data. This will cause the component to be re-rendered but with the updated value.
Here's an example of how you might do that:
import React, { useState, useEffect } from 'react';
import { Fade } from "#material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '#material-ui/core/styles';
import api from '../../services/api.js';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: '70%',
margin: 'auto',
marginTop: 20,
boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
}
}));
function User(props) {
const classes = useStyles();
const [checked, setChecked] = useState(false);
const [tableData, setTableData] = useState([]);
let config = {
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Sector', field: 'sector' },
{ title: 'E-mail', field: 'email'},
{ title: 'Tel', field: 'tel'}
],
actions: [
{ icon: 'create', tooltip: 'Edit', onClick: (rowData) => alert('Edit')},
{ icon: 'lock', tooltip: 'Block', onClick: (rowData) => alert('Block')},
{ icon: 'delete', tooltip: 'Delete', onClick: (rowData) => alert('Delete')},
{ icon: 'visibility', tooltip: 'Access', onClick: (rowData) => alert('Access')},
{ icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
],
options: {
headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
actionsColumnIndex: -1,
exportButton: true,
paging: true,
pageSize: 10,
pageSizeOptions: [],
paginationType: 'normal'
},
localization: {
body: {
emptyDataSourceMessage: 'No data'
},
toolbar: {
searchTooltip: 'Search',
searchPlaceholder: 'Search',
exportTitle: 'Export'
},
pagination: {
labelRowsSelect: 'Lines',
labelDisplayedRows: '{from} to {to} for {count} itens',
firstTooltip: 'First',
previousTooltip: 'Previous',
nextTooltip: 'Next',
lastTooltip: 'Last'
},
header: {
actions: 'Actions'
}
}
}
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
setTableData(response.data);
}
loadUsers();
}, [])
return (
<>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
<div className={classes.root}>
<MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={tableData} actions={config.actions}></MaterialTable>
</div>
</Fade>
</>
);
}
export default User;
I have a react-bootstrap-table-next where I have multiple columns. I want that when I click on edit button in column cell it open up a popover or select with 3 options:
Delete
Set as Default
Edit
here is the image attached.
Could someone please provide me a proper way to do it.
3 dots clicked and a list is opened for choices
Here is my code. In the Edit column there is event and onclick listener where I have to render the component.
export const Columns = (callback) => {
return ([{
dataField: "code",
text: "Code",
sort: true,
headerStyle: { width: "8%" }
},
{
dataField: "description",
text: "Description",
sort: true,
headerStyle: { width: "40%" }
},
{
dataField: "shortName",
text: "ShortName",
sort: true,
headerStyle: { width: "10%" }
},
{
dataField: "currencyCode",
text: "Currency",
sort: true,
headerStyle: { width: "10%" }
},
{
dataField: "isActive",
text: "Active/Inactive",
sort: false,
formatter: StatusSwitchRenderer,
headerStyle: { width: "12%" },
align: 'center',
attrs: { class: "cdmStatus" }
},
{
dataField: "dateAndTime",
text: "Last Modified Date",
sort: true,
formatter: Helper.completeDateFormat,
headerStyle: { width: "12%" }
},{
dataField: "isDefault",
text:"",
sort:false,
formatter: setDefault,
headerStyle: { width: "10%" },
style: (cell, row, rowIndex, colIndex) => {
if (row.isDefault) {
return {
backgroundColor: '#faede7'
};
}
return {
backgroundColor: '#e5f3fe'
};
}
},
{
dataField: "edit",
text: "Edit",
sort: false,
formatter: rankFormatter,
headerAttrs: { width: 50 },
attrs: { width: 50, class: "EditRow" },
events: {
onClick: (e, column, columnIndex, row, rowIndex) => {
console.log("Aya tou sahi")
}
}
}])
}