Get row data from MuiDataTable - javascript

I am trying to get the row data from MuiDataTable but when I set the index from the onRowSelectionChange function to a state, my checkbox animation stops working, here is how my options look:
const options = {
resizableColumns: true,
draggableColumns: {
enabled: true,
},
onRowSelectionChange: (rowData: any) => {
let rowIndex = rowData.map((e: any) => e.dataIndex);
setState(rowIndex)
},

onRowSelectionChange : (curRowSelected, allRowsSelected) => {
console.log("All Selected: ", allRowsSelected);
},
This will collect only index and dataIndex values
const [selectedFabrics, setSelectedFabrics] = useState([])
customToolbarSelect: selectedRows => (
<IconButton
onClick={() => {
// console.log(selectedRows.data)
const indexesToPrint = selectedRows.data.map((row, k) => row.dataIndex);
let temp = [];
for (let i = 0; i < fabricList.length; i++) {
if (indexesToPrint.includes(i)) {
temp.push(fabricList[i]['id']);
}
}
setSelectedFabrics(temp);
}}
style={{
marginRight: "24px",
height: "48px",
top: "50%",
display: "block",
position: "relative",
transform: "translateY(-50%)",
}}
>
<span style={{marginTop: "23px"}}>Print QR Code</span>
</IconButton>
),
Here fabricList is my total table raw data. From selecting rows index we can filter with actual fabricList index and push raw data to a new state.
Also if you want to remove the delete icon from top during multiple rows select. use it.
customToolbarSelect: () => {},

Related

Custom Column Searching & Filtering in Material Table

I have a column in my table called Active. Data in this column is returned as 1 or 0 for Active and Inactive.
These values are not displayed as 1 or 0. Instead, using the render prop, I have a function generateFlagText which looks for the value and renders an MUI Chip component.
Users cannot search the column by typing Active or Inactive, they would have to type 1 or 0, but they don't know this.
How can I write a check for Active or Inactive to display only those rows in the table using the customSearchAndFilter prop in Material-Table?
This is my column and function:
{
title: <Typography className={classes.colHeader}>Active</Typography>,
field: "Active",
render: (rowData) => generateFlagText(rowData.Active),
// customFilterAndSearch: (term, rowData) => console.log(term), // term is the user input
},
...
export function generateFlagText(status) {
if (status === 1)
return (
<Chip
label={"Active"}
style={{
backgroundColor: "#D9F5FD",
border: "1px solid #5CC1EE",
borderRadius: "4px",
color: "#0063B8",
}}
/>
);
if (status === 0)
return (
<Chip
label={"Inactive"}
style={{
backgroundColor: "#FFEBF5",
border: "1px solid #F69AC6",
borderRadius: "4px",
color: "#A8396F",
}}
/>
);
}
You can make something like this
const data = { type: "active" };
const rows = [
{ name: 1, active: 1 },
{ name: 2, active: 0 }
];
function f(data, rows) {
const mappedValues = {
active: 1,
inactive: 0
};
const currFilter = [];
Object.keys(mappedValues).forEach((key) => {
if (key.includes(data.type)) {
currFilter.push(mappedValues[key]);
}
});
return rows.filter((row) =>
currFilter.some((filterValue) => filterValue === row.active)
);
}
console.log(f(data, rows));
User can input any text and function return rows which includes symbols from ui table.
But it would be easier to make not an input, but a selection of two values in a selector

How can I seperate drop targets using React DnD

I'm using React Dnd v16.0.1. All containers and images being rendered are coming from JSON data and using map to render out elements. The problem I'm having is I have two droppable containers (which only one allows me to drop), when I drop an image on the droppable container that allows me both containers take up the same image dropped.
I just dragged an image to the right container which is the only one that allows me for some weird reason because im using map to render these containers from JSON data and as you can see both containers took up the image even though I dragged it to the right container.
Below is the code that renders the draggable images on the bottom and the black border drop targets containers. As you see below I'm using const [board, setBoard] = useState([]); which then I use to render the new images dragged, but how could i seperate this state per container?
const [board, setBoard] = useState([]);
let dragDropCounter = 0;
// eslint-disable-next-line react-hooks/rules-of-hooks
const [{ isOver }, drop] = useDrop({
accept: "image",
drop: (item) => addImageToBoard(item.id),
collect: (monitor) => ({
isOver: !!monitor.isOver()
})
});
const addImageToBoard = (id) => {
// console.log(id);
const pictureList = draggableItems.filter((item) => id === item.props.id);
console.log(pictureList);
setBoard((board) => [...board, pictureList[0].props]);
};
if (fields[0]) {
for (let i = 0; i < fields.length; i++) {
fields[i]?.drag?.map((dragData, x) => {
// eslint-disable-next-line react/jsx-key
draggableItems.push(
// <div>
<Img
fieldsPosX={dragData?.pos?.x}
fieldsPosY={dragData?.pos?.y}
fieldsWidth={dragData?.pos?.wid}
fieldsHeight={dragData?.pos?.ht}
type={dragData?.type}
id={x}
useReff={ref}
currentValue={number}
currentId={currentInputId}
url={dragData?.img}
/>
// </div>
);
dragDropCounter = x;
dragDropCounter++;
});
fields[i]?.drop?.map((dropData, index) => {
dropTargets.push(
<div
className="Board"
ref={drop}
key={dragDropCounter}
style={{
position: "absolute",
border: "2px solid black",
top: `${dropData?.pos?.x}px`,
left: `${dropData?.pos?.y}px`,
height: `${dropData?.pos?.ht}px`,
width: `${dropData?.pos?.wid}px`
}}>
{board.map((picture, index) => {
return (
<Img
key={index}
url={picture.url}
id={picture.id}
type={picture.type}
draggedElement={"true"}
/>
);
})}
</div>
);
dragDropCounter++;
});
// console.log(dropTargets);
}
return [draggableItems, dropTargets];
}
Draggable Image component:
const [{ isDragging }, drag] = useDrag(() => ({
type: "image",
item: { id: id },
collect: (monitor) => ({
isDragging: !!monitor.isDragging()
})
}));
return (
<>
{type === "IMG" ? (
<img
src={url}
ref={drag}
onClick={(e) => handleChange(e)}
id={`input-text${id}`}
name={"test"}
value={currentId == `input-text${id}` ? currentValue : null}
style={{
border: isDragging ? "5px solid pink" : "0px",
position: draggedElement ? "relative" : "absolute",
top: `${fieldsPosX}px`,
left: `${fieldsPosY}px`,
background: isDragging ? "pink" : "blue",
height: `50px`,
maxWidth: `50px`,
width: "100%",
zIndex: "40",
touchAction: "none"
}}
/>
Figured out how to have separate drop targets, so in my original code I had my drag component seperate from my drop component but once i combined the two components into one and used drag(drop(ref)) insead of drop(ref) and drag(reg) in those seperate files, my drop targets were now seperate and I was able to drop any image to any target without duplicating on both drop targets.

how to hide a row in mui data grid

I have a data grid using MUI. I want to hide a few rows and not display them based on a given condition and if a particular value exists in a column. How do I hide it? There seems to be props such as
hide
for columns but there is nothing for rows.
EDIT
The code is as follows
I want to hide the row from being displayed if a value exists in the 4th column (field: 'recvDistLocId')
and I want to hide the rows only when I press a button in the toolbar. So basically, its like a filter. Initially, all the data should be displayed and if I click the button, all rows with a value in the 4th column should disappear.
const columns = []
columns.push({
field: 'code',
headerName: nowrapHeader(appLanguages.serialCode[lang]),
flex: 1,
getApplyQuickFilterFn: getApplyFilterFnSameYear
});
columns.push({
field: 'brandId',
headerName: nowrapHeader(appLanguages.brand[lang]),
renderCell: (params) =>
getSelectCustomBodyRender(this.getBrandOptionMap(), params.row.brandId),
flex: 1,
});
columns.push({
field: 'slip',
headerName: nowrapHeader(appLanguages.slipNum[lang]),
renderCell: (params) => this.getSlipText(params.row.slip),
flex: 1,
});
columns.push({
field: 'recvDistLocId',
headerName: 'RecvDistLocId',
flex: 1,
hide: true,
});
/////This is the main data grid element code in the render()
<div style={{ height: 640, width: '100%' }}>
<DataGrid
sx={{
'& .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer': {
display: 'none',
},
}}
rows={serialsList || []}
columns={columns}
rowsPerPageOptions={[25, 50, 100]}
checkboxSelection={this.state.cancelShipFlag ? true : false}
disableSelectionOnClick={false}
components={{
Toolbar: NewToolbar,
}}
onSelectionModelChange={(ids) => {
const selectedIDs = new Set(ids);
const selectedRows = rowData.filter((row) => selectedIDs.has(row.id));
this.setState({ rowinfo: selectedRows });
this.setState({ selectedrows: ids });
//console.log('Selected rows: ' + this.state.selectedrows);
}}
selectionModel={this.state.selectedrows}
/>
</div>
filter the data based on the condition
// use setFilterValue to set the value of the clicked column e.g. recvDistLocId
const [filterValue, setFilterValue] = React.useState()
// assuming serialsList is array of strings
// use same case for comparing values
// use row.toLowerCase() !== filterValue.toLowerCase()
const rows = (serialsList || []).filter(row => row !== filterValue)
Then pass rows to the data grid
<DataGrid
rows={rows}
....

How to get value from array outside of loop

How can I get the value of oneHundredColorLevel from the array and render it outside the container of the other colors? Each set of colors should be the boxes for each color, with a separate box for the 100 level color.
Link to codepen
const colorPalette = {
Dark10: "#FEF9E8",
Dark20: "#FDF4DE",
Dark30: "#FCEFCC",
Dark40: "#F7E0A2",
Dark50: "#F0CC72",
Dark60: "#D5B363",
Dark70: "#C7A55E",
Dark80: "#B39553",
Dark90: "#9D8240",
Dark100: "#89723E",
Dark110: "#7C6737",
Dark120: "#715E32",
Dark130: "#66552E",
Dark140: "#5B4A28"
};
const ColorSwatches = ({ colors, shade }) => {
let filtered = Object.fromEntries(
Object.entries(colors).filter(([key, value]) => key.includes(shade))
);
let onehundredcolor = [];
return (
<div class="wrapper">
<div class="bigNumber">
<h1>{onehundredcolor}</h1><----SHOULD BE NAME ONLY
<p>{onehundredcolor}</p><----SHOULD BE HEX VALUE ONLY
</div>
{Object.entries(filtered).map((color, i) => {
if (i === 9) {
onehundredcolor.push(color);
console.log(onehundredcolor)
}else if(i !== 9) {
return (
<div key={i} style={{ backgroundColor: `${color[1]}` }}>
<h2>{color[0]}</h2>
<p>HEX {color[1]}</p>
</div>
);
}
})
</div>
);
};
I think that an array would be a more flexible container here than an object whose properties are the various shade/color pairs. For example:
const colors = [
{shade: 'dark', color: '#FEF9E8'},
{shade: 'dark', color: '#FDF4DE'},
...
{shade: 'light', color: '#111111'},
{shade: 'light', color: '#111122'},
...
];
Then you can filter/map as you see fit, for example:
const darkColors = colors.filter(c => c.shade === 'dark');
const lightColors = colors.filter(c => c.shade === 'light');
And you can easily access the 10th dark color via:
darkColors[9]

Populate dependent dropdownboxes in Express using MongoDB

Question
How do I create a series (e.g. 2) dropdownboxes that depend on the choice in the previous dropdown box, while both being populated from MongoDB queries from within the Express framework?
Example functionality
1. Create a MongoDB database with:
Volvo
Audi
Hyunday
2. Create a second database with the colors of the cars, e.g.
Volvo:yellow,red
Audi:ultra-violet,green
Hyunday:purple,blue
3. Show a webpage in localhost that displays a dropdown box with the 3 cars.
4. When the user selects a car e.g. Volvo, the second dropdownbox should be populated by
the MongoDB query that returns the colours of that car; e.g. yellow,red.
MWE
A complete MWE is found here, you can run it with: npm start.
Attempts
In response to the comments I have reduced my nr. of listed attempts to a single one. It contains the queries to the MongoDB whose results are displayed on the hosted website as well as a function that removes/renames entries from a dropdownlist. The attempt is written in file ../client/src/app.js and contains:
// /client/App.js
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
// initialize our state
state = {
data: [],
id: 0,
message: null,
intervalIsSet: false,
idToDelete: null,
idToUpdate: null,
objectToUpdate: null,
};
// when component mounts, first thing it does is fetch all existing data in our db
// then we incorporate a polling logic so that we can easily see if our db has
// changed and implement those changes into our UI
componentDidMount() {
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 2000000);
this.setState({ intervalIsSet: interval });
}
}
// never let a process live forever
// always kill a process everytime we are done using it
componentWillUnmount() {
if (this.state.intervalIsSet) {
clearInterval(this.state.intervalIsSet);
this.setState({ intervalIsSet: null });
}
}
// just a note, here, in the front end, we use the id key of our data object
// in order to identify which we want to Update or delete.
// for our back end, we use the object id assigned by MongoDB to modify
// data base entries
// our first get method that uses our backend api to
// fetch data from our data base
getDataFromDb = () => {
fetch('http://localhost:3001/api/getData')
.then((data) => data.json())
.then((res) => this.setState({ data: res.data }));
};
// our put method that uses our backend api
// to create new query into our data base
putDataToDB = (message) => {
let currentIds = this.state.data.map((data) => data.id);
let idToBeAdded = 0;
while (currentIds.includes(idToBeAdded)) {
++idToBeAdded;
}
axios.post('http://localhost:3001/api/putData', {
id: idToBeAdded,
message: message,
});
};
// our delete method that uses our backend api
// to remove existing database information
deleteFromDB = (idTodelete) => {
parseInt(idTodelete);
let objIdToDelete = null;
this.state.data.forEach((dat) => {
if (dat.id === idTodelete) {
objIdToDelete = dat._id;
}
});
axios.delete('http://localhost:3001/api/deleteData', {
data: {
id: objIdToDelete,
},
});
};
// our update method that uses our backend api
// to overwrite existing data base information
updateDB = (idToUpdate, updateToApply) => {
let objIdToUpdate = null;
parseInt(idToUpdate);
this.state.data.forEach((dat) => {
if (dat.id === idToUpdate) {
objIdToUpdate = dat._id;
}
});
axios.post('http://localhost:3001/api/updateData', {
id: objIdToUpdate,
update: { message: updateToApply },
});
};
// option 9:
// getOption(){
// var select = document.getElementById("dynamic-select");
// console.log(select);
// if(document.getElementById("dynamic-select").options.length > 0) {
// var option = document.getElementById("dynamic-select").options[document.getElementById("dynamic-select").selectedIndex];
// alert("Text: " + option.text + "\nValue: " + option.value);
// } else {
// window.alert("Select box is empty");
// }
// }
addOption(){
var inputElemAdd = document.getElementsByTagName('select');
var selectBox = document.getElementById("dynamic-select");
alert("Current len="+Object.keys(inputElemAdd));
alert("ID props="+Object.keys(selectBox)); // returns 3 props
alert("ID props 2="+Object.keys(selectBox[2])); // returns 3rd array element (index 2)
//alert("ID props 3 error="+Object.keys(selectBox[3])); // returns 3 props
alert("label="+ selectBox[2].label); // returns 3rd array element (index 2)
selectBox[0].label = "Wrote 0";
selectBox[2].label = "Wrote 2";
selectBox[3] = new Option('hi, added last label', 'id0',false,false); // add option
}
removeOption(){
var inputElem = document.getElementsByTagName('select');
for(var i = 0; i < inputElem.length; i++) {
inputElem[i].options[inputElem[i].selectedIndex] = null; // remove option
}
}
//
// removeAllOptions(){
// var select = document.getElementById("dynamic-select");
// select.options.length = 0;
// }
// here is our UI
// it is easy to understand their functions when you
// see them render into our screen
render() {
const { data } = this.state;
return (
<div>
<ul>
{data.length <= 0
? 'NO DB ENTRIES YET'
: data.map((dat) => (
<li style={{ padding: '10px' }} key={data.message}>
<span style={{ color: 'gray' }}> id: </span> {dat.id} <br />
<span style={{ color: 'gray' }}> data: </span>
{dat.message}
</li>
))}
</ul>
<div style={{ padding: '10px' }}>
<input
type="text"
onChange={(e) => this.setState({ message: e.target.value })}
placeholder="add something in the database"
style={{ width: '200px' }}
/>
<button onClick={() => this.putDataToDB(this.state.message)}>
ADD
</button>
</div>
<div style={{ padding: '10px' }}>
<input
type="text"
style={{ width: '200px' }}
onChange={(e) => this.setState({ idToDelete: e.target.value })}
placeholder="put id of item to delete here"
/>
<button onClick={() => this.deleteFromDB(this.state.idToDelete)}>
DELETE
</button>
</div>
<div style={{ padding: '10px' }}>
<input
type="text"
style={{ width: '200px' }}
onChange={(e) => this.setState({ idToUpdate: e.target.value })}
placeholder="id of item to update here"
/>
<input
type="text"
style={{ width: '200px' }}
onChange={(e) => this.setState({ updateToApply: e.target.value })}
placeholder="put new value of the item here"
/>
<button
onClick={() =>
this.updateDB(this.state.idToUpdate, this.state.updateToApply)
}
>
UPDATE
</button>
// Source: https://memorynotfound.com/dynamically-add-remove-options-select-javascript<br></br>-->
<select id="dynamic-select">
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
</select>
{/*<button onClick={this.getOption()}>get item</button>*/}
<button onClick={this.addOption}>add item</button> // remove the brackets to make it happen at onclick
{/*<button type="button" onClick={this.addOption}>Go</button>
<button onClick={this.removeOption()}>remove item</button>
<button onClick={this.removeAllOptions}>remove all</button>*/}
<br></br>
{/*//option 10
// source: https://stackoverflow.com/questions/27834226/add-event-listener-to-collection-of-html-elements*/}
<input class="inputs" type="submit" value="Hello" />
</div>
</div>
);
}
}
export default App;
Not sure why you need two different databases, I think two collections in one database is more fit, as in this case you can load all data required with aggregation. But then, how you formulated it, you have state with cars and colors, hide second dropdown of colors first, then on mount you already retrieving cars. After cars chosen, you make request to retrieve colors of given car and put it to state, which then make second dropdown appear.
getCars = async (carId) => {
try {
const carsResponse = await fetch(url, { method: 'POST', body: carId });
const cars = await carsResponse.json();
this.setState({ cars });
} catch (err) {
console.error(err);
}
}
getColors = () => {
// pretty same here
}
render () {
return (
<ul>
{this.state.cars.map((car) => {
return (
<li key={car.id} onClick={() => this.getColors(car.id)}>car.name</li>
);
})}
</ul>
{this.state.colors.length > 0 &&
<ul>
{this.state.colors.map((color) => {
<li key={color.id}>color.name</li>
})}
</ul>
}
);
}

Categories