Checkbox for specific row in react-table? - reactjs

import React, { Component } from 'react';
import { connect } from 'react-redux';
import getSchoolsList from '../Actions/Index';
import ReactTable from "react-table";
import checkboxHOC from "react-table/lib/hoc/selectTable";
import "react-table/react-table.css";
const CheckboxTable = checkboxHOC(ReactTable);
class Home extends Component {
constructor(props){
super(props);
this.state = {
selection: [],
selectAll: false
};
}
componentDidMount(){
this.props.getSchoolsList();
}
toggleSelection = (key, shift, row) => {
let selection = [...this.state.selection];
const keyIndex = selection.indexOf(key);
if (keyIndex >= 0) {
selection = [
...selection.slice(0, keyIndex),
...selection.slice(keyIndex + 1)
];
} else {
selection.push(key);
}
this.setState({ selection });
};
toggleAll = () => {
const selectAll = this.state.selectAll ? false : true;
const selection = [];
if (selectAll) {
const wrappedInstance = this.checkboxTable.getWrappedInstance();
const currentRecords = wrappedInstance.getResolvedState().sortedData;
currentRecords.forEach(item => {
selection.push(item._original._id);
});
}
this.setState({ selectAll, selection });
};
isSelected = key => {
console.log(key);
return this.state.selection.includes(key);
};
logSelection = () => {
console.log("selection:", this.state.selection);
};
render() {
const { toggleSelection, toggleAll, isSelected, logSelection } = this;
const { selectAll } = this.state;
const checkboxProps = {
selectAll,
isSelected,
toggleSelection,
toggleAll,
selectType: "checkbox",
};
const data = this.props.StateData?this.props.StateData.data:[];
const {loading, StateData} = this.props;
if (loading) {
{console.log(loading)}
return <div>Loading...</div>;
}
return (
<div>
{console.log(this.checkboxTable)}
<button onClick={logSelection}>Log Selection</button>
<CheckboxTable
ref={r => (this.checkboxTable = r)}
data={data}
columns={[
{
Header: "School Name",
accessor: "name"
},
{
Header: "Location",
id: "lastName",
accessor: d => d.area + ',' + d.city
},
{
Header: "Curriculum",
accessor: "curriculum"
},
{
Header: "Grade",
accessor:"grade"
},
{
Header: "Web App_URL",
accessor: "webapp_url",
},
{
Header: "Status",
id: "status",
accessor: d =>{
if(d.publish === true){
console.log(d.publish)
return 'Publish';
}else{
return 'Unpublished'
}
}
}
]}
defaultPageSize={10}
className="-striped -highlight"
{...checkboxProps}
/>
</div>
);
}
}
function mapStateToProps (state) {
return {
StateData:state.login.schools,
loading: state.login.loading,
}
};
export default connect(mapStateToProps, {getSchoolsList})(Home);
Hi all, can someone help me with this what is the wrong i am not getting individual checkboxes in this ? i checked this link code in my local it is working <https://codesandbox.io/s/7yq5ylw09j?from-embed>, but whenever i add my dynamic data it is not working.
Hi all, can someone help me with this what is the wrong i am not getting individual checkboxes in this ? i checked this link code in my local it is working <https://codesandbox.io/s/7yq5ylw09j?from-embed>, but whenever i add my dynamic data it is not working.
Hi all, can someone help me with this what is the wrong i am not getting individual checkboxes in this ? i checked this link code in my local it is working https://codesandbox.io/s/7yq5ylw09j?from-embed, but whenever i add my dynamic data it is not working.

If your using TypeScript and tslint this happens via the example for select table(checkboxes) getdata() does this:
const _id = chance.guid();
return {
_id,
...item
};
tslint complains about the _id var naming with "variable name must be in lowerCamelCase, PascalCase or UPPER_CASE"
You can see that at: https://react-table.js.org/#/story/select-table-hoc
So you have to change _id to id if you want to get past tslint. Changing from _id to id breaks the default keyField logic in react-table which wants _id. That necessitates setting the keyField property to "id".

If you do not mention unique key id by default it will take "_id" as the key field. By defining a key value you can overcome the above mentioned matter as follows.
Let's say there is a specific column named "USER ID". And we'll take the accessor of the column as "uid".
The code should be modified as follows.
Checkbox Table
<CheckboxTable
keyField="uid"
......Rest of your code....
/>
toggleAll()
toggleAll() {
..........code...........
currentRecords.forEach(item => {
selection.push(item.uid);
});
}
.......code............
}

Related

My cell renderer does not have any data because of an async call

So I have a component which uses a CellRenderer which gets some data:
import { useEffect, useMemo, useState } from "react";
import "ag-grid-community/dist/styles/ag-grid.min.css";
import "ag-grid-community/dist/styles/ag-theme-material.min.css";
import Grid from "../Common/Grid";
import axios from "axios";
import SelectJudetCellRenderer from "./SelectJudetCellRenderer";
function GetJudete() {
return axios
.get("http://localhost:5266/api/judete")
.then((response) => {
let data = response.data;
return data;
})
.catch((err) => {
console.log("Eroare la aducerea datelor.");
});
}
function Localitati() {
let [judete, setJudete] = useState([]);
useEffect(() => {
async function GetJudeteAsync() {
const result = await GetJudete();
setJudete(result);
}
GetJudeteAsync();
}, []);
const [columnDefs] = useState([
{ field: "nume", filter: "agTextColumnFilter", editable: true },
{ field: "judet", filter: "agTextColumnFilter", editable: true, cellRenderer: SelectJudetCellRenderer, cellRendererParams: {judete: judete} },
]);
return (
<Grid
baseLink="http://localhost:5266/api/localitati"
columnDefs={columnDefs}
/>
);
}
export default Localitati;
Here's my Cell renderer:
import { ICellRendererParams } from 'ag-grid-community';
export interface JudeteCellRendererParams extends ICellRendererParams {
judete: any[];
}
function SelectJudetCellRenderer(props: JudeteCellRendererParams) {
console.log(props.judete)
return (
<select name="judete">
{
props.judete.map((judet) =>
<option value={judet.id}>{judet.name}</option>
)
}
</select>
)
}
export default SelectJudetCellRenderer;
The problem is that after the Async call Judete is getting new data but my cell renderer does not get the new data.
The console.log() from the CellRenderer returns an empty array.
Why is this happening and how can I fix it?
Thanks.
You need to tell AG Grid to refresh the rendered cell, this is not very well documented, see https://www.ag-grid.com/javascript-data-grid/component-cell-renderer/#cell-renderer-component
Here is a simple example using Angular (should be similar for class based React)
Notice the refresh() method:
// gets called whenever the user gets the cell to refresh
refresh(params: ICellRendererParams) {
// set value into cell again
this.cellValue = this.getValueToDisplay(params);
}
https://plnkr.co/edit/yFqQHfNjxMLrPb9f.
For functional components you should explicitly call the api.refreshCells() when the data is available.
See here for more details: https://www.ag-grid.com/react-data-grid/component-cell-renderer/#component-refresh
A possible solution (although I think it would be more simple to switch to a class component renderer)
function Localitati() {
let [judete, setJudete] = useState([]);
// get hold of AG Grid gridApi
const gridApiRef = React.useRef<GridApi>();
// update the 'judete' column when new data is available (this will re-invoke the cell renderers)
useEffect(() => {
gridApiRef.current.refreshCells({columns: 'judet'});
}, [judete]);
useEffect(() => {
async function GetJudeteAsync() {
const result = await GetJudete();
setJudete(result);
}
GetJudeteAsync();
}, []);
const [columnDefs] = useState([
{ field: "nume", filter: "agTextColumnFilter", editable: true },
{ field: "judet", filter: "agTextColumnFilter", editable: true, cellRenderer: SelectJudetCellRenderer, cellRendererParams: {judete: judete} },
]);
return (
<Grid
baseLink="http://localhost:5266/api/localitati"
columnDefs={columnDefs}
onGridReady={({ api }) => {
gridApiRef.current = api;
}}
/>
);
}

Sorting a ReactTable based on a Switch being active

I need to sort a react table with the rows where a Switch is checked appearing at the top.
Here is the code I have so far.
import React from 'react';
import ReactTable from 'react-table';
import { useDispatch } from 'react-redux';
import { Switch } from '#material-ui/core';
import * as Actions from 'app/routes/actions/store/actions';
import { Pagination, FilterCaseInsensitive } from 'app/library';
export const PromotionControl = (props) => {
const dispatch = useDispatch();
const model = props.model;
const setModel = props.setModel;
const pageState = props.pageState;
const promotions = props.promotions;
function hasPromotion(promotion) {
if (model.actionPromotions === undefined)
return false;
const exists = model.actionPromotions.find(t => t.clientPromotionId === promotion.clientPromotionId);
return exists !== undefined;
}
function handlePromotionSelection(promotion) {
let selectedPromotions;
if (hasPromotion(promotion)) {
selectedPromotions = model.actionPromotions.filter(t =>
t.clientPromotionId !== promotion.clientPromotionId
);
}
else {
selectedPromotions = [...model.actionPromotions, {
clientPromotionId: promotion.clientPromotionId,
promotionName: promotion.promotionName
}];
}
setModel(values => ({ ...values, actionPromotions: selectedPromotions }));
}
return props.promotions.length <= 0 ? (<h5>No promotions configured.</h5>) : (
<React.Fragment>
<ReactTable
data={promotions}
PaginationComponent={Pagination}
defaultPageSize={5}
className="-striped -highlight"
minRows={0}
filterable={pageState.filterable}
sortable
filtered={pageState.filtered}
defaultFilterMethod={FilterCaseInsensitive}
onFilteredChange={(filtered) => dispatch(Actions.filterPromotions(filtered))}
columns={[
{
Header: "Client Promotion ID",
accessor: 'clientPromotionId',
width:200
},
{
Header: "Type",
accessor: 'promotionType',
maxWidth: 300
},
{
Header: "Name",
accessor: "promotionName"
},
{
maxWidth: 70,
style: { justifyContent: 'center' },
Cell: item => {
return (
<Switch
checked={hasPromotion(item.original)}
onClick={() => handlePromotionSelection(item.original)}
/>
)
}
}
]}
/>
</React.Fragment>
);
};
The column I want to sort the table by is the last column. At the moment the switch is checked based on the result of a check performed by the hasPromotion function. The issue is I don't know how to sort the whole table based on this column with the checked ones appearing at the top of the table.
Any ideas would be greatly appreciated.
As in Javascript, we have :
true - false === 1
false - true === -1
You could just base your sort on these values :
yourDataMatrix = yourDataMatrix.sort((a,b) => b.values[indexOfColumnToSort] - a.values[indexOfColumnToSort]);
Since I don't know the structure of your data, this code is not working, but you can use the logic in your code

How do I get BootstrapTable to redraw with updated data?

I have a Table from react-bootstrap that displays a list of devices with several columns. I want to change this so you can reorder by the columns so I switched to BootstrapTable from react-bootstrap-table-next. But my problem is that changing the state of the Component doesn't cause the table to update - this only happens if I click on a column to reorder the table.
The code is written to create the devices_table and save it to state then call an API to get the device version and add that to the state, causing the component to redraw. But when render() is called again the additional data isn't added to the table.
I've created a working example at https://codesandbox.io/s/react-bootstrap-table-next-new-data-problem-5w0op
import React, { Component } from 'react'
import BootstrapTable from 'react-bootstrap-table-next'
class DeviceTable extends Component {
constructor(props) {
super(props)
this.state = {
devices_table: {}
}
}
componentDidUpdate(prevProps) {
if (prevProps.devices !== this.props.devices) {
let devices_table = this.props.devices.map(this.myFunction);
this.setState({
devices_table: devices_table
})
}
}
myFunction = (value, index, array) => {
let device = {};
device.device = value;
device.index = index + 1;
device.version = '';
apiGetDeviceVersion(value.identifier)
.then((res, deviceId = value.identifier) => {
let devices_table = this.state.devices_table;
let objIndex = devices_table.findIndex(d => d.device.identifier === deviceId)
devices_table[objIndex].version = res.valueReported;
this.setState({
devices_table: devices_table
})
})
.catch(e => {
console.log(e)
})
return device;
}
render() {
const devices = this.state.devices_table;
if (isEmpty(devices)) {
return (<div></div>)
}
let columns = [
{
text: "#",
dataField: "index",
sort: true
},
{
text: "ID",
dataField: "device.identifier",
sort: true
},
{
text: "Name",
dataField: "device.name",
sort: true
},
{
text: "Status",
dataField: "device.status",
sort: true
},
{
text: "Version",
dataField: "version",
sort: true
}
];
return (
<div>
<BootstrapTable keyField={"device.identifier"} data={devices} columns={columns}></BootstrapTable>
</div>
)
}
}
export default DeviceTable
componentDidUpdate(prevProps) {
if (prevProps.devices !== this.props.devices) {
*let devices_table* = this.props.devices.map(this.myFunction);
this.setState({
devices_table: devices_table
})
}
}
It may happen due to response is working as asynchronous function(because of API). Thus, you have to wait for the response to make it available, otherwise "devices_table" remains empty or undefined and leads to no data visualization.

Using a function in Material-Table render property

I need to use a custom function in Material-Table column render property.
The function gets called, I get printed on the console the expected results, however, the result would simply not render in the table.
Here is the code:
import React from 'react';
import HraReferenceDataContext from '../context/hraReferenceData/hraReferenceDataContext';
import MaterialTable from 'material-table';
const EmployeeDetailsCompanyDocuments = ({ companyDocumentsData }) => {
const hraReferenceDataContext = React.useContext(HraReferenceDataContext);
const { companyDocumentTypes } = hraReferenceDataContext;
const getDocumentTypeForRow = id => {
companyDocumentTypes.forEach(type => {
if (type.id === id) {
console.log(type.name)
return type.name;
}
});
};
const columnInfo = [
{
field: 'typeId',
title: 'Type',
render: rowData =>{ getDocumentTypeForRow(rowData.typeId)}, //here is the problem
},
{ field: 'created', title: 'Created On' },
];
return (
<MaterialTable
columns={columnInfo}
data={companyDocumentsData}
title="Company Documents List"
/>
);
};
Returning inside forEach doesn't work.
change this function
const getDocumentTypeForRow = id => {
companyDocumentTypes.forEach(type => {
if (type.id === id) {
console.log(type.name)
return type.name;
}
});
};
to
const getDocumentTypeForRow = id => {
return companyDocumentTypes.find(type => type.id === id).name;
};
update
change
render: rowData =>{ getDocumentTypeForRow(rowData.typeId)},
to
render: rowData => getDocumentTypeForRow(rowData.typeId),
because you should return the value that is returned from getDocumentTypeForRow.

How to loop data and print in a table using material ui

I am working with Material-UI and getting data from the backend. There is no issue with the backend, but I don't know how to loop data and print it in a table format using Material-UI.
Can anyone guide me on how to print data in a table format?
Here is my code so far:
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { getProducts } from "../../services/products";
import MaterialTable, { MTableToolbar } from "material-table";
const productsList = props => {
const [data, setData] = useState([]);
const [state] = React.useState({
columns: [
{ title: "Brand", field: "brand" }, //assume here my backend schema is brand
{ title: "Price", field: "price" }, //here price
{ title: "Model no", field: "model" } //here model
]
});
const getProducts = async () => {
try {
const res = await getProducts();
setData(res.data);
console.log(res.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getProducts();
}, []);
return (
<MaterialTable
components={{
Toolbar: props => {
return (
<div>
<MTableToolbar {...props} />
</div>
);
}
}}
options={{
actionsColumnIndex: 5,
selection: true
}}
/>
);
};
export default function Company() {
return <productsList />;
}
You have to set the data and columns value. So try it like this:
import React, { useState, useEffect } from "react";
import MaterialTable, { MTableToolbar } from "material-table";
const fakeFetch = () => {
return new Promise(resolve => {
resolve({
data: [
{ brand: "brand 1", price: 1, model: "123" },
{ brand: "brand 2", price: 1, model: "456" },
{ brand: "brand 3", price: 1, model: "789" }
]
});
});
};
export default function App() {
const [data, setData] = useState([]);
// When the columns don't change you don't need to hold it in state
const columns = [
{ title: "Brand", field: "brand" }, //assume here my backend schema is brand
{ title: "Price", field: "price" }, //here price
{ title: "Model no", field: "model" } //here model
];
const getProducts = async () => {
try {
const res = await fakeFetch();
setData(res.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getProducts();
}, []);
return (
<MaterialTable
columns={columns} // <-- Set the columns on the table
data={data} // <-- Set the data on the table
components={{
Toolbar: props => {
return (
<div>
<MTableToolbar {...props} />
</div>
);
}
}}
options={{
actionsColumnIndex: 5,
selection: true
}}
/>
);
}
To make it even easier you could also provide your fetch function (fakeFetch in this case) as the data value;
data={fakeFetch} // <-- Using this you wouldn't need the [data, setData], getProducts and useEffect code.
Working sandbox link
As per the material-table approach, you have to put your whole fetched data on the data prop inside the MaterialTable component. So as far as I can understand, there is no looping made in this case by using the material-table library.
Assuming the attributes in your data object match the field names specified in your columns prop (if not, create an array of objects from your fetched data that matches the column fields or vice-versa).
And the code would be just the addition of the data prop in your table:
<MaterialTable
// ... existing props
data={data}
/>
Keep in mind that you could also use the remote data approach as described in the documentation which gives you the means to immediately query your data and fetch it inside the data prop of the table.

Resources