I want to create checkbox for every user in the table, this my tableBody :
<TableBody>
{users.map((user) => {
return (
<TableRow key={user.id}>
<TableCell padding="checkbox">
<Checkbox
checked={selectedUserIds.indexOf(user.id) !== -1} // check if the user.id is in selectedUserIds
onChange={() => handleSelectUser(user.id)} // evrytime I try to find the logic for this function I fail
value="true"
/>
</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.roles}</TableCell>
<TableCell>{user.status}</TableCell>
</TableRow>
);
})}
</TableBody>;
I have created this hook for storing the selected ids as shown in the example above
const [selectedUserIds, setSelectedUserIds] = useState([]);
The problem is that I can't logic for the function handleSelectUser that add/remove the user checked/unchecked in/from selectedUserIds I tried so many times and I failed.
If someone can help me I will be very thankful
const handleSelectUser = (id) => {
const selectedIndex = selectedUserIds.indexOf(id); // try to find the id in the array
let newSelectedUserIds = [];
if (selectedIndex === -1) { // if it does not exist
newSelectedUserIds = newSelectedUserIds.concat(selectedUserIds, id);
} else if (selectedIndex === 0) { // if it is the first element
newSelectedUserIds = newSelectedUserIds.concat(selectedUserIds.slice(1));
} else if (selectedIndex === selectedUserIds.length - 1) { // if it is the last element
newSelectedUserIds = newSelectedUserIds.concat(selectedUserIds.slice(0, -1));
} else if (selectedIndex > 0) { // found but in the midldle
newSelectedUserIds = newSelectedUserIds.concat(
selectedUserIds.slice(0, selectedIndex),
selectedUserIds.slice(selectedIndex + 1)
);
}
setSelectedUserIds(newSelectedUserIds);
};
learn more about Array.prototype.concat and Array.prototype.slice
I would try something like this:
const handleSelectUser = (userId) => {
// Check if the userId is already in selectedUserIds
if (selectedUserIds.includes(userId)) {
// If yes, remove it from selectedUserIds
setSelectedUserIds(selectedUserIds.filter((id) => id !== userId));
} else {
// If no, add it to selectedUserIds
setSelectedUserIds([...selectedUserIds, userId]);
}
};
Related
I'm building a train web booking project. Now I need to build a selectbox and the option value is varied based on the booking form.
//select-box
<custom-select-box
label="Travel"
v-model="form.InfantWithAdult"
:items="travel"
></custom-select-box>
Try with forEach:
travelWith() {
let travellerlist = [];
let adults = this.passengers.filter(
(x) =>
x.PaxType === "ADT" &&
x.SSR.Outbound.filter((y) => {
return y.codeType === "INFT";
}).length > 0
);
if (adults.length > 0) {
let nr = 1;
let travel = null;
// 👇 loop all adults and add to travellerlist array
adults.forEach(adult => {
if (!adult.FirstName) {
travel = `Adult ${nr++}`;
} else if (adult.FirstName !== "" && adult.LastName !== "") {
travel = `Adult ${nr++} ${adult.Title} ${adult.FirstName} ${adult.LastName}`;
}
travellerlist.push(travel);
})
}
return travellerlist;
},
When I click on the button, if the value of the button is 0, then the text will not show in that space. I mean, how can it be done without showing -10?
console.log(variable)
The this is the error I'm getting ..
const Mobile = () => {
const [charge, setCharge] = useState(20)
const setMobileCharge = () => setCharge(charge - 10)
if (setMobileCharge === 0) {
return 0;
}
return (
<div>
<h3>Charge : {charge}</h3>
<button onClick={setMobileCharge}>Battery down</button>
</div>
);
};
export default Mobile;
this happens because the react "ticks" your render is not fully synced with the state ... so to prevent that you can set the new state like that
setCharge(oldCharge => oldCharge -10 )
you can add a condition like below,
const Mobile = () => {
const [charge, setCharge] = useState(20)
const setMobileCharge = () => {
if(charge > 0) setCharge(charge - 10)} //here
if (setMobileCharge === 0) {
return 0;
}
return (
<div>
<h3>Charge : {charge === 0 ? "" : charge}</h3> //here
<button onClick={setMobileCharge}>Battery down</button>
</div>
);
};
I'm don't quite understand your question.
But if you want when 0 not to decrease, you can :
const setMobileCharge = () => {
if (charge > 0) {
setCharge(charge - 10)
}
}
If you want when 0 Mobile component disappear you can:
if (charge === 0) {
return 0;
}
I would like to sort table rows when clicking on table headers. I have the code working on a regular html/javascript snippet here. However it is not working as a React method. I think the problem is the use of the method .querySelectorAll() and .parentNode. I have already replaced the method .getElementById() with this.refs.rows by adding a ref to the <tbody> but I don't think one ref can point to multiple elements (to get all the <td> elements). I am looking for a similar way to replace both of the methods which don't seem to work. Or is it another problem entirely?
Edit:
Added the entire React component as asked in the comments
import React from "react";
import { Row, Col, Table } from "reactstrap";
export class ComparePlayers extends React.Component {
constructor(props) {
super(props);
this.state = {
players: [],
};
}
componentDidMount() {
fetch("/players")
.then((response) => response.json())
.then((state) => {
this.setState({ players: state });
});
}
handleSort(n) {
let rows, switching, i, x, y, shouldSwitch;
switching = true;
while (switching) {
switching = false;
rows = this.refs.rows; /* replaced document.getElementById("myTable").rows; */
for (i = 0; i < rows.length; i++) {
shouldSwitch = false;
x = rows[i].querySelectorAll("td")[n]; // how to replace?
y = rows[i + 1].querySelectorAll("td")[n]; // how to replace?
if (!isNaN(Number(x.innerHTML))) {
if (Number(x.innerHTML) > Number(y.innerHTML)) {
shouldSwitch = true;
break;
}
} else {
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); // how to replace?
switching = true;
}
}
}
render() {
return (
<Row>
<Col>
<Table bordered>
<thead>
<tr>
{this.state.players.length > 0 &&
Object.keys(this.state.players[0]).map((key, id) => (
<th
key={"header_" + key}
onClick={() => this.handleSort(id)}
>
{key.toUpperCase()}
</th>
))}
</tr>
</thead>
<tbody ref="rows">
{this.state.players.length > 0 &&
this.state.players.map((player, id) => {
return (
<tr key={"row" + id}>
{Object.values(player).map((value) => (
<td key={"table_value_" + value}>{value}</td>
))}
</tr>
);
})}
</tbody>
</Table>
</Col>
</Row>
);
}
}
UPDATE AND SOLUTION:
I used #MahdyAslamy 's answer and adapted to my state which is an array of objects. I used this tutorial to sort array of objects according to property values. Here is the final code:
handleSort(el) {
const compareValues = (key) => {
return function innerSort(a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
// property doesn't exist on either object
return 0;
}
const varA = typeof a[key] === "string" ? a[key].toUpperCase() : a[key];
const varB = typeof b[key] === "string" ? b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return comparison;
};
};
const sorted = this.state.players.sort(compareValues(el));
this.setState({
players: sorted
});
}
It's not good approach to sort table by changing tags position on dom. react suggest to use states and usual component life cycle for changing appearance.
for example:
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [5,6,8,9,6,5,4,22,4]
}
}
render() {
return (
<div className="game">
<button onClick={() => this.setState({ list: this.state.list.sort() })}>sort</button>
<ul>
{this.state.list.map(el => <li>{el}</li>)}
</ul>
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Only sort your list and then will show sorted list on render.
The points are:
when I enter any number in input so % sign add after the digit.
when I delete all numbers, % sign remove and width reset according entering the value.
if number already come from the api so when i click then my focus before % sign .
number <=100
so my functionality is working fine, but the problem is i am using multiple events (onClick, KeyPress, keyUp, onBlur etc) , so how i manage this functionality
<div className={`form-control valbox1 dvPercValue`} disabled = {isDisabled ? true : false } onClick={event=>this.focusPerc(event, rowIndex)}>
<Textbox
onTextboxChange={e => this.onTextboxChange(e, rowIndex)}
name="percentage"
isDisabled={isDisabled}
value={(value) }
maxLength={4}
refData={(input) => {
this.myAllocat[rowIndex] = input
}}
className = "percValue"
onInputKeyPress= {event=> this.setWidth(event, rowIndex) }
onInputkeyUp={ event=> this.setInputValue(event, rowIndex)}
textBoxWidth= { textBoxWidth }
/>
{ value && <span className="percSign ">%</span> }
</div>
const textBoxWidth = {
width: (value && parseInt(value,10) < 101) ? ((value.length ) * 8) : 0
};
focusPerc =(e, rowIndex) => {
if (rowIndex >=0) {
let searchInput= null;
const allBox = document.querySelectorAll(".dvPercValue");
for (let i=0;i<allBox.length;i++) {
allBox[i].classList.remove("active");
}
if (e.target && e.target.tagName) {
if (e.target.tagName.toLowerCase() === 'span' || e.target.tagName.toLowerCase() === 'input') {
e.target.parent.classList.add("active");
searchInput= e.target.parent.children[0];
} else {
e.target.classList.add("active");
searchInput = e.target.children[0]
}
}
if (searchInput) {
const strLength = searchInput.value.length * 2;
searchInput.focus();
searchInput.setSelectionRange(strLength, strLength);
}
}
}
setInputValue = (e, rowIndex) => {
const inputvalue=e.target.value.trim();
if (e.keyCode ===8 && rowIndex >=0) {
if (parseInt(inputvalue,10) > 100) {
return;
}
e.target.style.width = ((inputvalue.length ) * 8) + 'px';
}
}
setWidth =(e, rowIndex) => {
const inputval=e.target.value.trim();
if (parseInt(inputval,10) > 100) {
return;
}
e.target.style.width = ((inputval.length + 1) * 8) + 'px';
}
const TextboxComponent = (props) => {
return (
<input type="text
value= {props.value}
name= {props.name}
maxLength= {props.maxLength}
className={props.className}
disabled= {props.isDisabled}
placeholder= {props.placeHolder}
onChange={props.onTextboxChange}
onKeyPress = {props.onInputKeyPress}
onBlur = {props.onTextBoxBlur}
ref={props.refData}
autoComplete="off"
onKeyUp = {props.onInputkeyUp}
style = { props.textBoxWidth }
/>
)
}
For optimizing your ReactJS code you can firstly divide it into clear components and subsequently work on their functionality
So say you want add/remove a class "active" to your element so that you can highlight the field that the user is currently focused on this can be done as following:
<input
type="text"
className="simple"
onFocus={e => (e.target.classList += " active")}
onBlur={e => (e.target.className = "simple")}
/>
Which will help you avoid the current approach (which I guess is like jQuery) where you first capture all the elements and then loop over them.
Similarly if you want to capture the whole input of the user and then replace % sign for the deleted characters you can use a state for your component updating it through onChange.
If it is simply about showing a % sign after your input you can continue with your own code value && <span className="percSign ">%</span>
I am getting element by X, Y coordinates. I would like my final string result to look like Inspect -> copySelector:
body > div.layout.slidein-page-container > main > div.ng-scope > section.wrapper.wrapper--canvas > div > div > div > div:nth-child(2)
So it will be good queryString to evaluate JSHandle from it.
Here is my code:
let chosenElement = await page.evaluate((payload) => {
return document.elementsFromPoint(payload.data.x, payload.data.y)
.map((o) => {
if (o.id) {
return o.id;
} else if (o.className) {
if (o.className.includes(' ')) {
return o.tagName.toLowerCase() + '.' + o.className.split(' ').join('.');
} else {
return o.tagName.toLowerCase() + '.' + o.className;
}
} else {
return o.tagName.toLowerCase();
}
}).reverse().join(' > ');
}, payload);
So currently I am only checking for className and Id, but I want to make this more precise.
The first ting I want help is how do I check if element is nth-child(2) for example?
Can you help me with a better solution please
you can convert the element's parent children from HTMLCollection to an array with:
Array.from(o.parentElement.children)
find the index of your element:
const oIndex = Array.from(o.parentElement.children).indexOf(o);
return the selector:
return `${o.tagName}:nth-child(${oIndex + 1})`;
you need to add 1 because nth-child starts at 1, where indexes in array starts at 0;
BTW:
you forgot to add a # before the id when building the selector.
you don't need to lowercase the tagname in your selector
if the object has a className you can simply return ${o.tagName}.${Array.from(o.classList).join('.')}
I hope this will help some other guys
let chosenElement = await page.evaluate((payload) => {
return document.elementsFromPoint(payload.data.x, payload.data.y)
.map((o) => {
let sibArr = Array.from(o.parentNode.children).filter(i => i.tagName === o.tagName);
if (sibArr.indexOf(o) > 0) {
let oIndex = sibArr.indexOf(o);
return `${o.tagName.toLowerCase()}:nth-child(${oIndex + 1})`;
} else if (o.id) {
return '#' + o.id;
} else if (o.className) {
return `${o.tagName.toLowerCase()}.${Array.from(o.classList).join('.')}`
} else {
return o.tagName.toLowerCase();
}
}).reverse().filter(e => !e.includes('html')).join(' > ');
}, payload);
This meets the desired result:
body > div:nth-child(3) > header.layout__header.slidein-page > div.container > nav.nav-bar > ul.nav-bar__nav > li:nth-child(3) > a