Disable all checkboxes except "checked" react hooks - reactjs

I have listing of products and user can compare upto 4 products, when user checked 4 products I want to disable all checkboxes so user cannot select other product for compare until unless it uncheck one of 4 checkboxes.
const [checkedddItems, setCheckedItems] = useState({checkedItems : {}})
const handleComparePackage = (e, packageId) => {
const { id, checked } = e.target;
const updatedCheckedItems = comparedPackages.includes(packageId)? { [id]: checked } : {checkedddItems, [id] : checked }
console.log(updatedCheckedItems);
setCheckedItems({checkedItems: updatedCheckedItems})
}
{ insurancePackages.map((insPackage) => {
return (
<div className="col-lg-3 col-md-4 col-sm-6" key={insPackage.id}>
<div className="insurance-card active">
{compareSwitch &&
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
id={insPackage.id}
checked={checkedddItems[insPackage.id]}
disabled={!checkedddItems[insPackage.id]}
onChange={(e) => { handleComparePackage(e, insPackage.id) }} />
</div>
}
<div className="thumb">
<img src="/insurance/logo.svg" alt="logo" />
</div>
<div className="title">
{insPackage.company.name}
</div>
<div className="text-detail">
{insPackage.description}
<br />
<Link href="/">
<a>View Package Details</a>
</Link>
</div>
</div>
)
})
}

From what I can tell, it seems like you need some way to disable all checkboxes when the following conditions are met:
the checkbox is not checked,
the amount of total checked items > 3
This should simply turn into a simple boolean statement in the disabled attribute on the checkbox <input/>.
<input
className="form-check-input"
type="checkbox"
id={insPackage.id}
checked={checkedddItems[insPackage.id]}
disabled={!checkedddItems[insPackage.id] && checkedddItems.length > 3} // right here
onChange={(e) => { handleComparePackage(e, insPackage.id) }} />

Related

how to display items with category name as title

hi I have multiple items with different categories. I want to display all item with its Category name as a title at header. My code displaying each item but category Title repeats with each item.
I want Category title only one time at the top and then its items list.
MY Code is this
{
formsList.map((item, index,temp=0) => {
if(temp!==item.cat_id)
{
temp = item?.cat_id;
return (
<div className="custom-control custom-radio mb-3">
<div className="form-control-label"> {item.category_title}</div>
<input
className="custom-control-input"
id= {item.id}
name= {item.cat_id}
type="radio"
/>
<label className="custom-control-label" htmlFor={item.id}>
{item.form_title} {temp}
</label>
</div>
)
}
return (
<div className="custom-control custom-radio mb-3">
<input
className="custom-control-input"
id= {item.id}
name= {item.cat_id}
type="radio"
/>
<label className="custom-control-label" htmlFor={item.id}>
{item.form_title}
</label>
</div>
)
})
}
My Json array is like this.
{"forms":
[
{"id":1,"category_title":"Individual Tax Return","cat_id":1,
"form_title":"Single},
{"id":2,"category_title":"Individual Tax Return","cat_id":1,
"form_title":"Married Filing Separately"},
{"id":3,"category_title":"Business Type", "cat_id":2,
"form_title":"SoleProprietorships"},
{"id":4,"category_title":"Business Type","cat_id":2,
"form_title":" Partnership"}
]
}
I want to display this one like as below
//////////////////
Individual Tax Return
Single
Married Filing Separately
Business Type
SoleProprietorships
Partnership
/////////////////////////
Please check and help with thanks
Please try this
json part
const recipes = [{
id: 716429,
title: "Pasta with Garlic, Scallions, Cauliflower & Breadcrumbs",
image: "http://ovuets.com/uploads/716429-312x231.jpg>",
dishTypes: [
"lunch",
"main course",
"main dish",
"dinner"
],
recipe: {
// recipe data
}
}]
function part
export default function Recipes() {
return (
<div>
{recipes.map((recipe) => {
return <div key={recipe.id}>
<h1>{recipe.title}</h1>
<img src={recipe.image} alt="recipe image" />
{recipe.dishTypes.map((type, index) => {
return <span key={index}>{type}</span>
})}
</div>
})}
</div>
)}

every accordion opens when i click on any accordion : react js

every accordion opens when I click on any accordions can anyone help with this I want only one to open at a time
const [isActive, setIsActive] = useState(false);
<div className="accordion">
{Object.values(mapped).map((item, index) => (
//{predefined.map(({ date }) => (
<div className="accordion-item">
<div
className="accordion-title"
onClick={() => setIsActive(!isActive)}
>
<div>{item[0].date}</div> <div>11:45</div>
<div>painscale</div>
<div>4</div>
<div>{isActive ? "-" : "+"}</div>
</div>
{isActive && (
<div className="accordion-content">
<table>
<thead>
<tr>
<th>sdcs</th>
</tr>
</thead>
{item.map((e) => {
return (
<tr>
<td> {e.time}</td> <td>{e.d}</td>{" "}
<td> {e.scale}</td> <td>{escale.}</td>
</tr>
);
})}
</table>
</div>
)}
</div>
))}
</div>
every accordion opens when I click on any accordions can anyone help with this I want only one to open at a time
This is because the isActive state is a general one to the component and therefore would control all other elements in that component together.
However, to achieve what you're looking for, you can change that to an active current Index like so:
const [activeCurrentIndex, setActiveCurrentIndex] = useState();
const toggleShowAccordion = (id) => {
if(activeCurrentIndex === id){
setActiveCurrentIndex();
} else {
setActiveCurrentIndex(id);
}
}
//....
<div className="accordion">
{predefined.map(({ id, date, time , pain_scale, time_stamp})) => (
<div className="accordion-item" key={id}>
<div className="accordion-title" onClick={() => toggleShowAccordion(id)}>
<div>{date}</div>
<div>{isActive ? "-" : "+"}</div>
</div>
{activeCurrentIndex === id && <div className="accordion-content">{time} {pain_scale} {time_stamp}</div>}
</div>
))}
</div>
You're setting the activeCurrentIndex to that of the accordion you want to expand and you check if the activeCurrentIndex state is same as accordion's id to expand the menu otherwise, it's closed. You also need to supply a key to each element while mapping through an array

How can I pass calculated amount to state in react

How can I pass currentBook, currentUnits and calculated total during on click on the Record button
Now it just displays the entered value under the purchased book section. I would like to display the data and calculated amount during on click on Record button. Could someone please advise
ie Amount = units * price
for example it should display following result 1. Mathematics 6 300
https://codesandbox.io/s/falling-grass-gwpf9?file=/src/App.js
function App() {
const [currentBook, setCurrentBook] = useState('')
const [currentUnits, setCurrentUnits] = useState('')
const [currentPrice, setCurrentPrice] = useState('')
const [currentRecord, setCurrentRecord] = useState({book:'', units:'', price:''});
const changeBook = (newBook) => {
setCurrentBook(newBook);
}
const changeUnits = (newunits) => {
setCurrentUnits(newunits);
}
const changePrice = (newprice) => {
setCurrentPrice(newprice);
}
const calculateTotal = (e) => {
var cal_total = currentUnits * currentPrice;
setCurrentRecord(currentBook, currentUnits, cal_total );
//setCurrentRecord({ ...currentRecord, [e.target.name]: e.target.value });
}
return (
<div className="App">
<div>
<h1>Online Shopping</h1>
</div>
<div className="flexbox-container">
<div className="maintransaction">
<h3>Choose a book</h3>
<div className="container">
<select defaultValue={'DEFAULT'} onChange={(event) => changeBook(event.target.value)}>
<option value="DEFAULT" disabled>Choose a book ...</option>
<option value="maths">Mathematics</option>
<option value="science">Science</option>
<option value="english">English</option>
<option value="German">German</option>
</select>
</div><br></br>
<div className="quantity">
<span className="units">
<label>Units</label>
<input name="units" type="text" onChange={(event) => changeUnits(event.target.value)}></input>
</span>
<span className="price">
<label>Price</label>
<input name="price" type="text" onChange={(event) => changePrice(event.target.value)}></input>
</span>
</div>
<div className="recordBtn">
<button onClick={(event) => calculateTotal()}>Record</button>
</div>
</div>
<div className="purchasedbooks">
<h3>Purchased book:</h3>
<table>
<tr>
<th>Item no</th>
<th>Books</th>
<th>Units</th>
<th>Price</th>
</tr>
{
//currentRecord.map(({ currentBook, currentUnits }) => (
<tr>
<td>1.</td>
<td>{currentBook}</td>
<td>10</td>
<td>250</td>
</tr>
// ))
}
</table>
</div>
</div>
</div>
);
}
export default App;
Several changes have been made in the sandbox link
Calculate the total when click record as asked in the question
Add some checking when user press the record button. Three inputs need to be filled in.
The input type should be number but not text since it may need to NaN if user enter string in the input.
Implementing a button to reset all the record.
Use map for rendering currentRecord
It looks like your map should look like this:
{currentRecord.map(item => (
<tr>
<td>1.</td>
<td>{item.currentBook}</td>
<td>{item.units}</td>
<td>{item.price}</td>
</tr>
))
}

patchValue returning only values of the last index

I have an issue while looping through and array, I'm getting all the index from this array correctly but when I use angular patchValue it updates all the inputs with the last index values and not with their respective values as shown :
I want every input to have theirs, for example, first input values should be "1" (left input => longueur) and "1" (right input => quantity)
I tried with .forEach but no success
CODE SAMPLE
component.ts .forEach
ngOnInit() {
this.requiredFields();
this.service.checkExistQuot().subscribe(res => {
this.quotDetails.forEach( (myArray, index) => {
this.dropForm.patchValue({
longueur: this.quotDetails[index].longueur,
quantity: this.quotDetails[index].quantity
})
console.log(index);
});
});
}
HTML, input example
<div class="products">
<div class="d-flex flex-row" *ngFor="let products of selectedDiam;let i = index">
<input class="number" formControlName="longueur" value="" (change)="postQuotationDatas(products.part_id)" type="number">
</a>
<input class="mb-1 flex1 checkbox" type="checkbox">
<a class="tac flex1"></a>
<a class="flex1 mb-1">
<input class="number" value="" formControlName="quantity" (change)="postQuotationDatas(products.part_id)" type="number">
</a>
<a class="flex1"></a>
</div>
</div>
Your problem is that you only have one form group,dropForm, with 2 controls: quantity and longueur. Even though you have multiple html inputs for longueur and quantity, they are share the same reference in the component
So, with your forEach loop, you are actually patching all your controls for each iteration. That's why you have the same value for all your controls, which is the value for the lasst object the loop went over.
Option #1
A possible solution is to have multiple form groups, like in this stackblitz example
component.ts
//dropForms initialisation
this.quotationDetails.map(() =>
{
let group = this.formBuilder.group(
{
longueur: [''],
quantity: [''],
});
this.dropForms.push(group)
}
this.quotationDetails.forEach( (myArray, index) => {
this.dropForms[index].patchValue({
longueur: this.quotationDetails[index].longueur,
component.html
<div class="d-flex flex-row" *ngFor="let products of quotationDetails; let index=index">
<form [formGroup]="dropForms[index]"> <!-- 1 form group per quotation -->
Option #2
The other solution, to have only 1 formGroup, is to give dynamic control names
component.ts
//Initialisation of dropForm
this.quotationDetails.forEach((val, index)=>
{
group[`longueur_${index}`] = '';
group[`quantity_${index}`] = ''
});
this.dropForm = this.formBuilder.group(
group
)
//Patch
this.quotationDetails.forEach( (myArray, index) => {
let patchValue = {};
patchValue[`longueur_${index}`] = this.quotationDetails[index].longueur;
patchValue[`quantity_${index}`] = this.quotationDetails[index].quantity;
this.dropForm.patchValue(patchValue);
component.html
<form [formGroup]="dropForm">
<div class="products">
<div class="d-flex flex-row" *ngFor="let products of quotationDetails; let index = index">
<a>
<input class="number" formControlName="longueur_{{index}}" value="" type="number">
Stackblitz example
Use FormArray and wrap inputs in arrays.
Example add FormControl to FormArray with FormBuilder:
FormArray - A FormArray aggregates the values of each child FormControl into an array.
in componenent ts:
const EMPLOYEE_FORM = {
firstName: ['', Validators.required],
lastName: ['', Validators.required],
isActive : [false, Validators.required]
}
const COMPANY_FORM = {
employees: new FormArray([], [Validators.required])
}
export class AppComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group(COMPANY_FORM);
}
get employees(): FormArray {
return this.form.get('employees') as FormArray;
}
addEmployee() {
const employee = this.fb.group(EMPLOYEE_FORM);
this.employees.push(employee);
}
}
in html
<div *ngFor="let item of employees.controls; let i = index;">
<form [formGroup]="item">
<input type="text" formControlName="firstName" class="form-control" placeholder="FirstName">
<input type="text" formControlName="lastName" class="form-control" placeholder="LastName">
<input class="checkbox" type="checkbox" formControlName="isActive">
</form>
</div>
<button (click)="addEmployee()">add new employee</button>
<div *ngIf="employees.length > 0">
{{employees.value | json}}
</div>
See this link: https://stackblitz.com/edit/add-input-to-formarray-with-frombuilder

React JS - element focus on input not working

I'm having trouble with focusing element programmatically.
I have a ul which looks like this :
<ul className="text-left">
{Object.keys(props.characters).map((char, i) => {
return (
<li key={props.characters[char].key}>
<button type="button" className={"btn align-middle bg_" + props.characters[char].character}>
<div className={"label text-left float-left " + getFontColorFromCharacter(props.characters[char].character)}>
<img alt="" className="char-img" src={"/images/characters/" + props.characters[char].character + "_xs.png"}/>
<input className="align-middle d-none" id={props.characters[char].key + "_input"} type="text" placeholder="Nom joueur" value={props.characters[char].player_name} onChange={e => props.changePlayerNameHandler(props.characters[char],e)} onBlur={e => toggleDNone(props.characters[char].key)} onKeyDown={e => tabToNext(e)}/>
<span className="align-middle" id={props.characters[char].key + "_span"} onClick={e => toggleDNone(props.characters[char].key)} > {props.characters[char].player_name}</span>
</div>
<div className={"actions " + getFontColorFromCharacter(props.characters[char].character)}>
<span className="action">
<FontAwesomeIcon icon="times-circle" title="Supprimer" onClick={() => props.removeCharacterHandler(props.characters[char].key)}/>
</span>
</div>
</button>
</li>
);
})}
</ul>
Javascript :
//Toggle d-none class on input & span for player name edition
function toggleDNone(key) {
document.getElementById(key + "_input").classList.toggle("d-none");
document.getElementById(key + "_span").classList.toggle("d-none");
if (!document.getElementById(key + "_input").classList.contains("d-none")) {
document.getElementById(key + "_input").focus();
}
}
//When the user hit tab key, navigate to next input
function tabToNext(event){
if(event.key === "Tab")
{
var allInput = document.querySelectorAll("[id$='_input']");
var indexOfCurrent = Array.from(allInput).indexOf(event.target);
var id;
if (indexOfCurrent + 1 === Array.from(allInput).length)
{
id = allInput[0].id;
}
else
{
id = allInput[indexOfCurrent + 1].id;
}
toggleDNone(allInput[indexOfCurrent].id.replace("_input", ""));
toggleDNone(id.replace("_input",""));
}
}
When the users click on the span, the input is displayed and the focus is working. When the users hit the tab key to get to the next input, the input is displayed but the focus is not working.
I tried setting the tabIndex to -1 as I saw on a post but it didn't work.
Any ideas?
Found the solution.
The issue was that the second toggleDNone call started before the first one finished.
I just added a setTimeOut of 100ms on the second call and it worked.
Thanks all

Resources