I am trying to create material-table with switches, that onclick changes the state of the component.
Here is component with table, which has state of the parent component as a props. I pass rows variable to the material table data prop. Switch is custom rendered field. Whenever I click on it, it triggers changeRow, which finds index of row in rows variable changes it and saves into new variable. ChangeRows is then called to change the state.
The problem is, the switch is not changing. It looks like nothing is happenning, even though I can clearly see new state in console.
const StuffTable = ({rows, changeRows}) => {
const changeRow = (oldRow, e) => {
const changeData = {[e.target.name]: e.target.checked};
const newRow = {...oldRow, ...changeData};
console.log(oldRow, e);
const index = rows.findIndex(dtaRow => dtaRow.id === oldRow.id);
const newData = rows;
newData[index] = newRow;
console.log(newData);
changeRows(newData);
};
return (
<Container maxWidth="lg">
<Button onClick={() => { changeRow({id: 6}, { target: {name: 'borrowable', checked: true} }) }}>klikni</Button>
<MaterialTable
options={{
actionsColumnIndex: -1,
search: true,
exportButton: true,
exportDelimiter: ";"
}}
actions={[
{
icon: 'edit',
tooltip: 'Edit Study',
onClick: (event, rowData) => alert("Do you want to edit?")
}]}
columns={[
{ title: "Název", field: "title" },
{ title: "Stav", field: "status", render: (data) => <Chip label={data.status} color="primary" avatar={<Avatar src="/static/images/avatar/1.jpg" />} /> },
{ title: "Půjčovat", field: "borrowable", render: (data, id) => (<FormControlLabel control={<Switch checked={data.borrowable} onChange={(e) => changeRow(data, e)} name="borrowable" color="primary"/>} label={data.borrowable ? 'půjčovat' : 'nepůjčovat'} />) },
{ title: "Vidí všichni", field: "active", render: (data, id) => (<FormControlLabel control={<Switch checked={data.borrowable} onChange={(e) => changeRow(data, e)} name="borrowable" color="primary"/>} label={data.borrowable ? 'půjčovat' : 'nepůjčovat'} />) },
{ title: "Uskladněno", field: "location" },
]}
data={rows}
title="Moje věci"
/>
</Container>
);
};
export default StuffTable;
I tried to add button, which on click changes state to empty array, and table did show nothing. But when I triggered changeRow (mockup data) with this button, result was the same - no change on the switch.
import React, {useEffect, useState} from 'react';
import StuffTable from "../components/stuffTable";
let rows = [
{id:5, title: "prošívanice", borrowable: false, surname: "Baran", status: "zapůjčeno", location: "Praha" },
{id:6, title: "prošívanice 2", borrowable: false, surname: "Baran", status: "zapůjčeno", location: "Praha" },
{id:7, title: "prošívanice 3", borrowable: false, surname: "Baran", status: "zapůjčeno" , location: "Brno"}
];
Here is Parent component
const MyStuffPage = () => {
const [data, setData] = useState(rows);
return (
<div>
<StuffTable rows={data} changeRows={(data) => {setData(data); console.log("hou",data);}} />
</div>
);
};
export default MyStuffPage;
Here is Codesandbox with this problem:
https://codesandbox.io/s/festive-gould-i1jf7
You need to call onQueryChange whenever you want to render new data or state to the datatable, make these changes:
at the begining create a ref like so:
const tableRef = useRef(null);
then use it in the material table:
<MaterialTable
//add this
tableRef={tableRef}
options={{
actionsColumnIndex: -1,
search: true,
exportButton: true,
exportDelimiter: ";"
}}
then inside your changeRow function after updating the start and the necessary work add this:
tableRef.current.onQueryChange()
this will tell the table to render the new data with the correct state of the switch
Related
I'm learning React.js and this is a table showing which user has which items.
I would like to have a button for each item and delete the corresponding item.
How do you have or {FaTrash} icon in a const object?
This is my full code below
const columns = [
{
name: "Username",
selector: "username",
sortable: true
},
{
name: "Email",
selector: "email",
sortable: true
},
{
name: "Item",
selector: "items",
sortable: true,
right: true
},
{
name: "Action",
value: <button>Edit</button>
}
]
const Admin = () => {
const [data, setData] = useState(allUsers);
const handleRowClicked = row => {
const updatedData = data.map(item => {
if (row.id !== item.id) {
return item;
}
return {
...item,
toggleSelected: !item.toggleSelected
};
});
setData(updatedData);
}
return ( <>
<div className='users p-5'>
<DataTable
title="Users"
columns={columns}
data={data}
defaultSortField="title"
pagination
onRowClicked={handleRowClicked}
/>
</div>
</> );
}
export default Admin;
I used to pass a function that returns a piece of layout with handler
{
name: "Action",
actionRenderer: ({ index, item }) => {
return (
<button onClick={() => onhandle(item)}>
ActionName <!--or icon component-->
</button>
)
}
},
Than you need to create <DataTableRow> component wich will render each object in your columns array. Somewhere in the <DataTableRow> you will be able to access to actionRenderer and your data item:
<div>{actionColumn.actionRenderer({ index, item })}</div>
I need to display data on the antd table based on the selected item from select option.The data that are to be displayed are stored in different variables. For example, if school is selected from select option then the datasource is available in schoolData and similarly for other option.
Here's my code:
import React, { useState } from "react";
import Framework from "../framework/Framework";
import { Dropdown, Button, Table, message, Select } from "antd";
import { DeleteOutlined, DownOutlined, EditOutlined } from "#ant-design/icons";
import { Content } from "antd/lib/layout/layout";
import AddNewButton from "../addNewButton/AddNewButton";
import "./attributes.css";
import DataSource from "./Datasource";
import IconDescription from "../icondescription/IconDescription";
import Modal from "antd/lib/modal/Modal";
import { Option } from "antd/lib/mentions";
const Attributes = () => {
const [page, setPage] = useState(1);
const [isModalVisible, setIsModalVisible] = useState(false)
// const [layer, setLayer] = useState()
const columns = [
{
title: "S.N",
dataIndex: "key",
},
{
title: "Name",
dataIndex: "name",
key: "name",
// render: (text) => <a>{text}</a>,
},
{
title: "Address",
dataIndex: "address",
key: "address",
},
{
title: "Contact No.",
dataIndex: "contactno",
key: "contactno",
},
{
title: "Operation",
dataIndex: "operation",
key: "operation",
render: () => {
return (
<div style={{ display: "flex" }}>
<IconDescription icon={<EditOutlined />} label="Edit" />
<IconDescription icon={<DeleteOutlined />} label="Delete" />
</div>
);
},
},
];
const addAttribute = () => {
setIsModalVisible(true)
}
const modalHandleOk = () => {
setIsModalVisible(false);
};
const modalHandleCancel = () => {
setIsModalVisible(false);
};
const selectLayer = (e) => {
console.log("select layer", e)
}
return (
<Framework>
<Content className="attributes">
<div className="select-addNewBtn-container">
<Select defaultValue="school" style={{ width: 120 }} onChange={selectLayer}>
<Option value="school">School</Option>
<Option value="hospital">Hospital</Option>
<Option value="policeStation">Police Station</Option>
</Select>
<AddNewButton name={"Add New Attribute"} addNewBtn={addAttribute} />
<Modal title={"Add New Attribute"} visible={isModalVisible} centered onCancel={modalHandleCancel} onOk={modalHandleOk}>
</Modal>
</div>
<Table
dataSource={DataSource}
columns={columns}
className="data-table"
pagination={{
size: "small",
pageSize: 6,
hideOnSinglePage: true,
showSizeChanger: false,
}}
/>
</Content>
</Framework>
);
};
export default Attributes;
How do i achieve the desired functionality? Do let me know. Quite a beginner at such things.
Assuming Datasource is just a simple array of data objects, and presumably you have another file eg: 'SchoolData' that you need to switch between when the select option is chosen.
What I'd do to keep it simple is create a react state variable to wrap your data and just set it at will in the onChange of your select.
Example
I have two values at data index: isOnline (true and false). Right now <NavLink /> renders on both true and false, but I want to render only when value is false and not when it's true. Is there any possibility to do that?
I want to hide hyperlink if value is true. Hyperlink should only be rendered with false value. At the moment it gives me hyperlink on both true and false. But I want true value not to be linked with any other stuff but false value should be clickable.
import React, { useEffect, useState } from 'react';
import {
Tabs, Icon, Divider, Table, Button, Input, Row, Col,
} from 'antd';
import moment from 'moment';
import { NavLink, Redirect } from 'react-router-dom';
import { getAllLeads } from '../../shared/services/apiService';
import { isSuccess } from '../../shared/utils/jsHelper';
import routePaths from '../../shared/routePaths';
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
getCheckboxProps: (record) => ({
disabled: record.name === 'Disabled User', // Column configuration not to be checked
name: record.name,
}),
};
const dateFormat = 'YYYY-MM-DD';
const Leads = () => {
const [leadList, setLeadList] = useState([]);
const columns = [
{
title: 'Sl no.',
dataIndex: 'name',
render: (name, record) =><span>{leadList.indexOf(record) + 1}</span>
},
{
title: 'Date',
dataIndex: 'createdAt',
},
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Phone number',
dataIndex: 'phone_number',
},
{
title: 'Email id',
dataIndex: 'email',
},
{
title: 'Type',
dataIndex: 'type',
},
{
title: 'Comment',
dataIndex: 'comment',
},
{
title: 'Property name',
dataIndex: 'propertyId.name',
//render: (name, row) => {row.propertyId ? (<NavLink to={`${routePaths.PROPERTY}/${row.propertyId._id}`}>{name}</NavLink>) : null},
},
{
title:'On-Line / Off-Line',
dataIndex: 'isOnline',
// render: (isOnline, row) => {
// if(!isOnline){
// <NavLink to={`${routePaths.LEADSFORM}/${row._id}`}>
// {isOnline}
// </NavLink>
// }else{null}
// }
render: (isOnline, row) => <NavLink to={`${routePaths.LEADSFORM}/${row._id}`}>{isOnline}</NavLink>,
}
];
const [loading, setLoading] = useState(false);
var tokenSession = localStorage.getItem('token');
if(tokenSession!='undefined' && tokenSession == null){
var setTokenSession = true;
}else{
var setTokenSession = false;
}
const [loggedDashOut, setDashLoggedOut] = useState(setTokenSession? true:false);
if (loggedDashOut) {
return <Redirect to={routePaths.LOGIN} />;
}
const getLeadsList = () => {
setLoading(true);
getAllLeads().then((resp) => {
if (isSuccess(resp)) {
setLeadList(resp.data.data);
}
}).finally(() => setLoading(false));
};
useEffect(() => {
getLeadsList();
return () => {};
}, []);
return (
<Row className="row-pad">
<Col xs={24} sm={24} md={8} lg={8} xl={8}><h3>Leads</h3></Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}></Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<NavLink to={routePaths.LEADSFORM}>
<Button type="primary" className="btn-add">Add Leads</Button>
</NavLink>
</Col>
<Divider />
<Table pagination={{ pageSize: 10 }} columns={columns} dataSource={leadList} />
</Row>
);
};
export default Leads;
I'm not sure where is your isOnline variable, but basically if you want to render something conditionally you can try:
return (
...
{isOnline ? null : <NavLink />}
...
)
This will render the <NavLink /> component only when isOnline is false.
This is not the only way to implement conditional rendering in React, just a very common one, and I think it suits your use case.
For more details you can refer to the official docs regarding conditional rendering.
Good luck!
I am trying to manage state with react hooks for my checkBOx toggle but I am failing to manage different state for different checkbox, with my code I am trying "ON" & "OFF" for toggle but when I am trying to toggle any checkbox its changing states for all of my checkboxes all at once.
following is my code :
import React,{useState} from 'react';
import { Box, Checkbox, Flex, Table, Txt } from 'rendition';
import styled from 'styled-components';
const ControlContainer = styled(Box)`
border-top-left-radius: 10px;
`;
export const Devices = () => {
const [checked, setChecked] = useState(true);
function toggle(event) {
return setChecked(event.target.checked ? setChecked(false) : setChecked(true));
}
const SAMPLE_DATA = [
{
id: 1,
name: 'Balcony',
active: true,
brightness: 50,
},
{
id: 2,
name: 'Bedroom 01',
active: false,
brightness: 70,
},
{
id: 3,
name: 'Bedroom 02',
active: false,
brightness: 70,
},
];
const columns = [
{
field: 'name',
label: 'Room',
sortable: true,
},
{
field: 'active',
label: 'State',
sortable: true,
render() {
return (
<Flex>
<Checkbox toggle checked={checked} onChange={toggle} mr={2} />
<Txt ml={2}>{checked ? 'On' : 'Off'}</Txt>
</Flex>
);
},
},
{
field: 'brightness',
label: 'Brightness',
sortable: true,
render(value) {
return `${value}%`;
},
},
];
return (
<Flex flex='1' mt={4}>
<Box flex='3' pl={3}>
<Table
flex='1'
columns={columns}
data={SAMPLE_DATA}
rowKey='id'
onRowClick={console.log}
/>
</Box>
</Flex>
);
};
PS : Getting following warnings :
Each child in a list should have a unique key prop.
Failed prop type: You provided a checked prop to a form field without an
onChange handler. This will render a read-only field. If the field should be
mutable use defaultChecked. Otherwise, set either onChange or readOnly.
So, after getting lots of suggestions and a downvote finally I got a solution.
It is divided into three parts.
first I passed SAMPLE_DATA as useState
const [data,setdata]=useState(SAMPLE_DATA)
Secondly, I made some changes in my checkbox by adding a check on the field : row.active
<Flex>
<Checkbox toggle
checked={row.active}
mr={2} id={`checkbox-${row.id}`}
data-rowid={row.id}
onChange={toggle}/>
<Txt ml={2}> {row.active ? 'on' : 'off'} </Txt>
</Flex>
at last, I altered my toggle function accordingly
const toggle=(event)=> {
let rowId = event.target.getAttribute('data-rowId')
let newData = data.map(item => {
if(item.id == rowId) {
item.active = event.target.checked
}
return item;
})
setdata(newData)
}
Thank you all for your valuable time and suggestions.Topic Closed
I'm using antd's table component. I want to be able to filter the nodes down to any descendant.
I'm following the example of antd's custom filter.
The example is not a tree structure, so here's a codesandbox with the right setup.
onFilter seems to only loop over the root nodes. I don't know how to display Tesla > Model 3 it I type '3' in the search input. I know I have to return true for Tesla, but I still don't understand how to then also return true for Model 3
data
const data = [
{
key: "13",
name: "Tesla",
data: {
description: "Car manufacturer",
type: "Organization"
},
children: [
{
key: "4444",
name: "Model S",
data: {
description: "The fastest",
type: "Project"
}
},
{
key: "1444323",
name: "Model 3",
data: {
description: "The cheapest",
type: "Project"
}
}
]
},
{
key: "1",
name: "Microsoft",
data: {
description: "#1 software company",
type: "Organization"
}
}
];
App
class App extends React.Component {
state = {
searchText: '',
};
getColumnSearchProps = (dataIndex) => ({
filterDropdown: ({
setSelectedKeys, selectedKeys, confirm, clearFilters,
}) => (
<div style={{ padding: 8 }}>
<Input
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm)}
/>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm)}
icon="search"
>
Search
</Button>
</div>
),
// onFilter seems to only loop over root nodes
onFilter: (value, record) => record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
render: (text) => (
<Highlighter
...
/>
),
})
render() {
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
...this.getColumnSearchProps('name'),
}];
return <Table columns={columns} dataSource={data} />;
}
}
You can write a function to get a node's descendant values (based on dataIndex) and filter for any of those having the search text too.
This would go inside of getColumnSearchProps before the return value:
const getDescendantValues = (record) => {
const values = [];
(function recurse(record) {
values.push(record[dataIndex].toString().toLowerCase());
record.children.forEach(recurse);
})(record);
return values;
}
...and this would be the updated onFilter:
onFilter: (value, record) => {
const recordName = record[dataIndex] || record.data[dataIndex];
const searchLower = value.toLowerCase();
return recordName
.toString()
.toLowerCase()
.includes(searchLower)
||
getDescendantValues(record).some(descValue => descValue.includes(searchLower));
}
I've forked your sandbox here: https://codesandbox.io/s/10jp9wql1j
...it includes all the root nodes with any descendant that satisfies your search condition. However it does not filter out the other descendant nodes that do not satisfy your search condition, unfortunately.
After some searching, it seems like there isn't a great way to do this.
I'm using mobx and React, but the basic principles can be applied elsewhere. The idea is to give the rows you want to filter out a CSS class name to hide them.
I used mobx to create an observable of the checked filter values. You could also use React.useState. Using your example, let's say the observable is called ModelFilters. Then, on the table component, add rowClassName:
<Table
...
rowClassName={(record, index) => {
if (ModelFilters.includes(record.model) ) {
return ''
}
return 'some-css-class-with-display-none'
}}
/>
Any record that doesn't have a model that matches the selected filters will be hidden. Of course, if you are using the table to manipulate/submit data, you will have to handle that elsewhere. This is just for displaying purposes.