**Here is the cart imports and state
const Cart = ({ cart, setActiveTab, removeFromCart, updateQuantity }) => {
const [selectedRows , setSelectedRows] = useState([]);
const [toggleCleared, setToggleCleared] = useState(false);
const [products, setProducts] = useState(cart.products) }
const columns = [
{
name: 'Product Name',
selector: 'name',
sortable: true,
},
{
name: 'Product Price',
selector: 'price',
sortable: true,
right: true,
},
{
name: 'Product Quantity',
selector: 'quantity',
sortable: true,
right: true,
cell: row => <Fragment><input type="number" className="form-control" value={row.quantity} onChange = {(e)=>updateQuantity( row.id,strong text e.target.value)}/></Fragment>
},
{
name: 'Subtotal',
selector: 'quantity',
sortable: true,
right: true,
cell: row => <Fragment>{row.quantity * row.price}</Fragment>
},
{
name: 'Actions',
selector: 'quantity',
sortable: true,
right: true,
cell: row => <Fragment><i className="fa fa-trash" aria-hidden="true" onClick = {(e)=>removeFromCart(row.id)}></i></Fragment>
},
];
const contextActions = useMemo(() => {
const handleDelete = () => {
selectedRows.forEach(item=>{
console.log(item, 'item')
removeFromCart(products, products.indexOf(item))
});
setProducts(products);
setToggleCleared(!toggleCleared);
console.log('handleDelete', products)
};
return <i className="fa fa-trash" key="delete" onClick={handleDelete} style={{ backgroundColor: 'red' }} ></i>;
});
This code sets the selected rows when we select rows
function rowSelection (rows) =>{
setSelectedRows(rows.selectedRows);
}
return ( <DataTable
title="Cart Products"
columns={columns}
data={products}
selectableRows
onSelectedRowsChange={rowSelection}
clearSelectedRows={toggleCleared}
contextActions={contextActions} />
)
**here is the my cart component which works fine but it does not updates itself when the redux state changes.. i can see the redux state in console that it changes perfectly but datatable does not rerenders
const [products, setProducts] = useState(cart.products) } this will only works on initial rendering ( called initial state).useState will not call once the render is completed. so just pass the cart.products in to the datatable
function rowSelection (rows) =>{
setSelectedRows(rows.selectedRows);
}
return ( <DataTable
title="Cart Products"
columns={columns}
data={cart.products}
selectableRows
onSelectedRowsChange={rowSelection}
clearSelectedRows={toggleCleared}
contextActions={contextActions} /> )
Related
I am using antd to create a table with filter feature,
but I got a problem, I want to custom filter search of antd to call api,
antd table
How can I do that with react and antd,
Here is my code
const columns: ColumnsType<Product> = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (name, record) => (
<div className="flex items-center">
<Avatar src={record.avatar}>{name[0].toUpperCase()}</Avatar>
<span style={{ marginLeft: 10 }} className="whitespace-nowrap">
<a title="View & Edit" href={'/product/' + record.id}>
{name}
</a>
</span>
</div>
),
},
{
title: 'About',
dataIndex: 'about',
key: 'about',
render: (about: string) => <p className="text-ellipsis line-clamp-2">{about}</p>,
width: 400,
},
{
title: 'Categories',
dataIndex: 'categories',
key: 'categories',
filterSearch: true,
filters: categories!.map((category: Category) => ({ text: category.name, value: category.id })),
render: (categories: any) => {
console.log(categories);
return '-';
},
// width: 400,
},
{ title: 'Addresses', dataIndex: 'contract_addresses', key: 'address', render: addresses => addresses?.[0] },
];
To filter the table, I usually provide a function that filters the data before it is passed to the table (In my example the function getData. This function can also be used to adjust the filter accordingly. Here is an example, which also consists of a Search-Input to modify the search:
const CustomersTable = (props: any) => {
const [searchText, setSearchText] = useState<string | undefined>(undefined);
const [customers, setCustomers] = useState<ICustomer[]>([]);
const getData = (
sourceData: Array<ICustomer>,
searchText: string | undefined
) => {
if (!searchText) {
return sourceData;
}
return sourceData.filter((item) => {
const comparisonString = `${item.firstname.toLowerCase()}${item.familyname.toLowerCase()}`;
//here you can provide a more sophisticared search
return comparisonString.includes(searchText.toLowerCase());
});
};
const columns = [
{
title: "Vorname",
dataIndex: "firstname",
key: "firstname",
sorter: (a: ICustomer, b: ICustomer) => {
if (a.firstname.toLowerCase() > b.firstname.toLowerCase()) {
return -1;
} else if (a.firstname.toLowerCase() < b.firstname.toLowerCase()) {
return 1;
} else {
return 0;
}
},
},
{
title: "Nachname",
dataIndex: "familyname",
key: "familyname",
},
];
return (
<>
<Search
placeholder="Vorname, Nachname"
value={searchText}
onChange={(e) => {
setSearchText(e.target.value);
}}
/>
<Table
columns={columns}
dataSource={getData(customers, searchText)}
/>
</>
);
};
I am trying to implement crud functionalities in Ant design table. I observed that the delete and edit functionalities only works on the instance when I perform the operations on initial render, but after reloading the component, the table returns back to its initial state and the operations don't affect the database in any way.
I see this error on my console
Type '{ title: string; dataIndex: string; key: string; align: string; editable: boolean;
render?: undefined; }' is not assignable to type 'ColumnType<any>'.
Types of property 'align' are incompatible.
Type 'string' is not assignable to type 'AlignType'.
These are the codes below, I hope to break them down in code blocks so they can be understandable
imports
import React, { useState, useEffect } from 'react';
import { Table, Popconfirm, Button, Space, Input, Form } from 'antd';
import { isEmpty } from 'lodash';
api
const apiUrl = 'api/terminals';
useState and useEffect codes
const [gridData, setGridData] = useState([]);
const [loader, setLoader] = useState(false);
const [editingKey, setEditingKey] = useState('');
const [editRow, setEditRow] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
setLoader(true);
const response = await axios.get(apiUrl);
setGridData(response.data);
setLoader(false);
};
modifiedData codes
const modifiedData = gridData.map(({ ...item }) => ({
...item,
key: item.id,
}));
const save = async key => {
try {
const row = await form.validateFields();
const newData = [...modifiedData];
const index = newData.findIndex(item => key === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
setGridData(newData);
setEditingKey('');
}
} catch (error) {
console.warn('Error', error);
}
};
edit and cancel function code block
const edit = record => {
form.setFieldsValue({
name: '',
stock: '',
stockLevelDate: '',
tankThreatLevel: '',
tankThreatLevelColor: '',
tankTopPosition: '',
tankTopPositionColor: '',
lowPumpable: '',
lowPumpableColor: '',
tankCapacity: '',
...record,
});
setEditingKey(record.key);
};
const cancel = () => {
setEditingKey('');
};
editableCell function block
const EditableCell = ({ editing, dataIndex, title, record, children, ...restProps }) =>
{
const input = <Input />;
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
required: true,
message: `Please input ${title}`,
},
]}
>
{input}
</Form.Item>
) : (
children
)}
</td>
);
};
editing code block
const isEditing = record => {
return record.key === editingKey;
};
columns block
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
},
{
title: 'Name' as string,
dataIndex: 'name',
key: 'name',
align: 'center',
editable: true,
},
{
title: 'Stock',
dataIndex: 'stock',
key: 'stock',
align: 'center',
editable: true,
},
{
title: 'Stock Level Date',
dataIndex: 'stockLevelDate',
key: 'stockLevelDate',
align: 'center',
editable: true,
},
{
title: 'Tank Threat Level',
dataIndex: 'tankThreatLevel',
key: 'tankThreatLevel',
align: 'center',
editable: true,
},
{
title: 'Tank Threat Level Color',
dataIndex: 'tankThreatLevelColor',
key: 'tankThreatLevelColor',
align: 'center',
editable: true,
},
{
title: 'Tank Top Position',
dataIndex: 'tankTopPosition',
key: 'tankTopPosition',
align: 'center',
editable: true,
},
{
title: 'Tank Top Position Color',
dataIndex: 'tankTopPositionColor',
key: 'tankTopPositionColor',
align: 'center',
editable: true,
},
{
title: 'Low Pumpable',
dataIndex: 'lowPumpable',
key: 'lowPumpable',
align: 'center',
editable: true,
},
{
title: 'Low Pumpable Color',
dataIndex: 'lowPumpableColor',
key: 'lowPumpableColor',
align: 'center',
editable: true,
},
{
title: 'Tank Capacity',
dataIndex: 'tankCapacity',
key: 'tankCapacity',
align: 'center',
editable: true,
},
{
title: 'Actions',
dataIndex: 'actions',
key: 'actions',
align: 'center',
render: (_, record) => {
const editable = isEditing(record);
return modifiedData.length >= 1 ? (
<Space>
<Popconfirm title="Sure to delete?" onConfirm={() => handleDelete(record)}>
<Button type="primary" disabled={editable} danger>
Delete
</Button>
</Popconfirm>
{editable ? (
<span>
<Space size="middle">
<Button onClick={e => save(record.key)} type="primary" style={{ marginRight: 8
}}>
{' '}
Save
</Button>
<Popconfirm title="Sure to cancel?" onConfirm={cancel}>
<Button>Cancel</Button>
</Popconfirm>
</Space>
</span>
) : (
<Button onClick={() => edit(record)} type="primary">
Edit
</Button>
)}
</Space>
) : null;
},
},
];
mergedCoulumns block
const mergedColumns = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
handleDelete code block
const handleDelete = value => {
const dataSource = [...modifiedData];
const filteredData = dataSource.filter(item => item.id !== value.id);
setGridData(filteredData);
};
return jsx
<>
<div>
<h2>Terminals</h2>
<Link to={`${match.url}/new`} className="btn btn-primary jh-create-entity" id="jh
-create-
entity" data-cy="entityCreateButton">
<FontAwesomeIcon icon="plus" />
Add Terminal
</Link>
<hr color="red" />
<Form form={form} component={false}>
<Table
components={{
body: {
cell: EditableCell,
},
}}
columns={mergedColumns}
dataSource={modifiedData}
bordered
loading={loader}
/>
</Form>
</div>
</>
Thanks in anticipation
I am using react hooks and have a dynamically filled array of values after a backend call. How can I then insert these values (which I don't know a priori) in a checkbox?
I get some properties from another component.
These properties change based on a selection from a table.
I would like to be able to add / remove these properties based on a selection on a checkbox when I open the "Available roles list"
const UsersList = (props) => {
const { loadedRoles } = props;
const [checked, setChecked] = useState([]);
const selectHandler = (Event, selected) => {
loadedRoles(selected.roles);
};
const handleChange = (Event) => {
setChecked({ ...props.roles, [Event.target.name]: Event.target.checked });
};
return (
<div>
<MaterialTable
title=" Users List"
data={props.users}
columns={[
{ title: "User Id", field: "id" },
{ title: "Active", field: "active" },
{ title: "System", field: "system" },
{ title: "Roles", field: "roles" },
]}
options={{
cellStyle: {
width: 100,
minWidth: 100,
height: 1,
},
headerStyle: {
width: 100,
minWidth: 100,
},
}}
onRowClick={selectHandler}
/>
<Card>Current Role: {props.current}</Card>
<Card>
<label>Available Roles: {props.roles}</label>
<Checkbox name={props.roles} onChange={handleChange} />
</Card>
</div>
I can assume that you receive from your backend an array of objects, containing the label, keys, and values for the checkboxes?
[
{ label: 'Checkbox #1', key: 'checkbox1', checked: true },
{ label: 'Checkbox #2', key: 'checkbox2', checked: false },
{ label: 'Checkbox #3', key: 'checkbox3', checked: true },
]
You can make the call to the API in a useEffect, store the result in a local state via useState, then iterate through the values in the rendering:
const [checkboxes, setCheckboxes] = useState(null)
useEffect(() => {
fetch('/your/api')
.then(res => res.json())
.then(checkboxes => setCheckboxes(checkboxes))
}, [])
return (
<ul>
{checkboxes === null
? <li>Loading...</li>
: checkboxes.map(checkbox => (
<li key={checkbox.key}>
<label>
<input type="checkbox" name={key} defaultChecked={checkbox.checked} />
{checkbox.label}
</label>
</li>
))
}
</ul>
)
The checkboxes here are not controlled (defaultChecked). To use controlled checkboxes instead, you’ll need to create another local state and initialize it from the values returned by the backend, and use checked & onChange props instead.
I try to add delete button to my grid row, So I use cellRendererFramework and every thing is fine until state variable return undefined in my root component
I set my state variable with useEffect and every things seems to be OK and my grid shows data correctly
let [roles, SetRoles] = React.useState<Role[]>();
React.useEffect(() => {
List().then((roles) => {
console.log("set roles count:" + roles.data.length);
SetRoles(roles.data);
});
return () => {
console.error("Component will unmount!");
};
}, []);
ColDefs defination:
const columnDefs = [
{
headerName: "کلید",
field: "id",
flex: 0,
hide: true,
},
{
headerName: "عنوان",
field: "title",
flex: 2,
sortable: true,
sort: "asc",
},
{
headerName: "توضیحات جدید",
field: "description",
flex: 6,
},
{
headerName: "حذف",
field: "Id",
flex: 1,
cellRendererFramework: function (row: any) {
return (
<Button
danger
onClick={() => {
onDeleteClick(row.data);
}}
>
حذف
</Button>
);
},
},
];
and my onDeleteHandler and my PROBLEM is
const onDeleteClick = (data: any) => {
//this line works correctly and Id of my row pass to my event
console.log(data.id);
//Issue is here, my loaded state variable in Undefined, But my other function like count of roles work correctly
console.log(roles);
};
<Button
danger
onClick={() => {
onDeleteClick(row.data['id']);
}}
>
حذف
</Button>
I have a table which displays clinics. I have also a onPageChange prop which handles the pageIndex and then i fetch the data based on that page. Below is my table configuration
import 'react-table/react-table.css'
import React, { Component } from 'react';
import ClinicFormComponent from './newClinicForm';
import SearchFormComponent from '../search/searchForm';
import { connect } from 'react-redux';
import { fetchClinics, fetchClinic, deleteClinic, searchClinics, pushBreadcrumb, popBreadcrumb } from '../../actions/index.js';
import { toast } from 'react-toastify';
import ReactTable from 'react-table'
import store from '../../helpers/store';
import { ic_search } from 'react-icons-kit/md/ic_search';
import SvgIcon from 'react-icons-kit';
require('normalize.css/normalize.css');
// require('styles/App.css');
class ClinicsPage extends Component {
constructor() {
super();
this.handleClickForm = this.handleClickForm.bind(this);
this.handleClickFormSearch = this.handleClickFormSearch.bind(this);
this.closeForm = this.closeForm.bind(this);
this.onPageChange = this.onPageChange.bind(this);
}
componentDidMount(){
this.props.searchClinics({ country: 'Australia' });
}
onPageChange(pageIndex) {
this.props.fetchClinics(pageIndex, null);
}
render() {
let { devs } = this.props;
const columns = [{
Header: 'Id',
accessor: 'id' // String-based value accessors!
}, {
Header: 'Name',
accessor: 'name'
}, {
Header: 'Description', // Required because our accessor is not a string
accessor: 'description',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Country', // Required because our accessor is not a string
accessor: 'country',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Area', // Required because our accessor is not a string
accessor: 'area',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Latitude', // Required because our accessor is not a string
accessor: 'latitude',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Longitude', // Required because our accessor is not a string
accessor: 'longitude',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Tags', // Required because our accessor is not a string
accessor: 'tags',
sortable: true,
Cell: (row) => {if (row.original.tags.length>1) { return row.original.tags.join(', ') } else { return row.original.tags } }
},
{
Header: () => <span className="text-center">Actions</span>,
accessor: 'id',
id: 'actions',
sortable: true,
Cell: (row) => (<span><button className="text-center btn btn-primary-zoetis margin_right_5" onClick={()=>{this.props.history.push('/editClinic/'+ row.original.id)}}>Edit</button ><button className="text-center btn btn-danger" onClick={()=>{this.props.deleteClinic(row.original.id, row.original)}}>Delete</button ></span>)
}
]
return (
<div className="wrap">
<div className="row margin_top_10 margin_bottom_5">
<div className="col-sm-6">
<a className="btn btn-link color_zoetis btn_zoetis_alt" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapseExample2" onClick={this.handleClickForm}>NEW CLINIC</a>
</div>
<div className="col-sm-6">
<a className="nav-link float_right search-clinic-btn" data-toggle="collapse" onClick={this.handleClickFormSearch} role="button" aria-expanded="false" aria-controls="collapseExample"><span className="search_label">Search data entries...</span> <SvgIcon size={25} icon={ic_search}/></a>
</div>
</div>
<div id="nav-tabContent">
<ReactTable
data={devs.clinics}
pageSizeOptions= {[10]}
defaultPageSize= {10}
columns={columns}
pages={devs.paginationData.totalPages || ''}
sortable={true}
multiSort={true}
//manual
filterable
page={devs.paginationData.pageNumber}
loading={devs.isFetching}
onPageChange={this.onPageChange}
noDataText='No Data Found'
className='-striped -highlight'
/>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
devs: state.reducer.devs,
showMenu: state.reducer.devs.showMenu,
showEditMenu: state.reducer.devs.showEditMenu,
paginationData: state.reducer.devs.paginationData,
location: state.router.location.pathname
}
}
export const mapDispatchToProps = {
fetchClinics,
searchClinics,
fetchClinic,
deleteClinic,
pushBreadcrumb,
popBreadcrumb
}
export default connect(mapStateToProps, mapDispatchToProps)(ClinicsPage);
Notice that i have disabled manual prop. If i enable the manual prop then i can navigate through next and previous pages but i cannot sort or filter the data.
With the manual prop disabled the filtering and the sorting works correct but when i navigate in the next page the table is showing empty. The first page displays correct the first 10 data. Also i have tested the api and returns correct the next 10 data.
Is there any workaround? To keep both server side pagination and alse the default sorting and filtering?
Test decomposing manual property
<ReactTable
filtered={this.state.filtered}
onFilteredChange={this.onFilteredChange.bind(this)}
defaultFilterMethod={(filter, row) =>
String(row[filter.id]) === filter.value
}
columns={columns}
ref={r => (this.selectTable = r)}
className="-striped -highlight"
defaultPageSize={10}
data={this.state.data}
pages={this.state.pages}
loading={this.state.loading}
manual <-------
resizable={true}
filterable
filterAll={true}
onFetchData={(state, instance) => {
this.setState({loading: true})
axios.get(`${API}${controller}`, {
params: {
pages: state.page,
pageSize: state.pageSize,
filtered: state.filtered,
typeOption: `${type}`,
dateinit: `${this.state.dateinit}`,
datefinal: `${this.state.datefinal}`,
data: this.props.data
},
headers: { 'Authorization': `${tokenCopyPaste()}` }
})
.then((res) => {
this.props.exportTable(res.data, type);
this.setState({
data: res.data.data,
fulldata: res.data,
pages: Math.ceil(res.data.pages / state.pageSize),
loading: false
})
})
}
}
/>