Limiting the number of checkboxes selected in react - javascript

currently I have a signup form with 5 options but I'm trying to find a way to limit so the user can only select 2 options and in case the user selects a third option the first one would be unchecked, I had found a way of doing this in plain js but I haven't found a react way of doing it. This is what I have so far, would it be better to handle with plain js instead of react?
{iconsPool.map((src, index) => (
<Box className="test">
<input type="checkbox" className="iconsCheckbox" id={iconsPool.id} />
<label for={iconsPool.id}>
<img className="signupIcons" src={iconsPool[index].src} key={index} />
</label>
{console.log(iconsPool)}
</Box>
))}

This can be implemented with a state as an array with 2 elements.
Two items of the state Array will represent the index of selected items.
If an checkbox is clicked, that checkbox and the one clicked right before will be checked. (Therefore unchecking the one that was clicked even before that)
This can be done by pushing the index of newly clicked checkbox into the head of array, and removing the last item of the array.
When an checked checkbox is clicked again, (therefore it should be unchecked,) the index of the checkbox is searched from the state array, and removed by replacing that value with undefined
Below is code, as an example
...
const [checkedItems, setCheckedItems] = useState([undefined,undefined])
// When an Item is clicked
const onClickItem = (itemIndex:number, isSelected: boolean) =>{
if(isSelected){
setCheckedItems(c=>([itemIndex,c[0]]))
} else {
if(itemIndex === checkedItems[0]){
setCheckedItems(c=>([undefined,c[1]]))
} else if(itemIndex === checkedItems[1]){
setCheckedItems(c=>([c[0],undefined]))
}
}
}

Related

How to get custom number buttons to show on correct input field react.js

Having a hard time seeing how I could accomplish this. I created some custom number buttons from 0-9 that users can click on instead of using the keyboard. The problem I'm having is I have multiple dynamically created input fields depending on JSON Data so let's say there are 10 dynamically created input fields and a user starts with question one and the user then uses the custom number buttons I created and clicks numbers "145" to answer question one, but what happens is then all 10 inputs have the same number "145" not the problem the user was trying to solve. I'm using the context API to then save the values typed in on a function called getButtonValue that I then call to the parent component and save the values in a state array, so I know that my problem is that all the inputs share the same state array but how could I make sure the correct input the user clicks on is only receiving those values.
Thanks in advance.
My Custom Number Button Component:
import { FormContext } from "../../lib/FormContext";
function ActivityBar() {
const { getButtonValue } = useContext(FormContext);
return (
<div className={`${activity.activity__workSheet__numberButton}`}>
<button value={0} onFocus={(e) => getButtonValue(e)}>
<img
className={`${activity.activity__workSheet__img0}`}
src={"/assets/activityNumber-btn.png"}
alt="activity number button"
/>
.... more code
Parent Component:
const [numberButtonClicked, setNumberButtonClicked] = useState([]);
const getButtonValue = (e) => {
setNumberButtonClicked((prevButtonClicked) => [
...prevButtonClicked,
e?.target?.attributes[0].value
]);
};
return (
<Carousel>
<div ref={imageRef} style={{ height: "100%" }}>
{Object.entries(elements).map((element, i) => {
const { fields } = element[1];
if (fields) {
return (
<Element
key={i}
field={fields[0]}
id={i}
useReff={`answer${i}`}
currentValue={
numberButtonClicked === "" ? null : numberButtonClicked.join("")
}
/>
);
} else {
return;
}
})}
</div>
</Carousel>
Got a good working version figured out for this scenario, what I did was.
I have a onFocus method on my input tags that then takes in the event and calls a handleChange(e) function. Within that function I then save the currentInputId in a variable by using e?.target?.attributes[0]?.value and the previous InputId in a state variable and just check if the previous InputId is equal to the currentId user just focused on. If so then we'll add the next number user clicks into the same field, else if previousInputId !== currentInputId then make my user value state array empty, setNumberButtonClicked([]).

Input radio button's defaultChecked attribute prevents onClick function from being triggered

<section key={i}>
<input
type='radio'
key={attribute.name + item.id}
id={attribute.name + item.id}
name={attribute.name}
value={item.value}
defaultChecked={i === 0}
onClick={(event) => inputClick(attribute, event)}
/>
<label
htmlFor={attribute.name + item.id}
>
{
item.value
}
</label>
</section>
The code above is more or less what a .map() function is supposed to return in my React.js APP, creating input radio buttons for customizing a product before adding it to the cart state. All I did was remove some classes, etc. that were there for CSS purposes, etc.
Ideally, what is supposed to happen here is that...
When rendering the product page for the first time, the very first input radio button that is returned by .map() should be checked by default. This works thanks to the "defaultChecked" attribute. No problems here.
After I click a different input radio button (not the "defaultChecked" one), the checked input should change and all the other inputs should be left unchecked. As I understand it, input radio buttons with the same 'name' attribute do this automatically.
When clicking the input radio button, the "onClick" function should trigger. This is where the code is not functioning as I wish it would.
The issue is that when my page renders for the first time and I click on an input radio button (other than the "defaultChecked") the "onClick" function does not trigger. It only triggers when I have clicked on a different input once and then on another.
A.k.a. it triggers on the second click. After that second click, it works as intended - every time a different input radio button is selected/clicked on - the function triggers, but not for the very first time.
I tested this with console.log("I am triggered") at the end of the "onClick" "inputClick(attribute, event)" function, and only on the second click would the console log "I am triggered".
I was able to fix the issue by removing the "defaultChecked" attribute. I think the issue might be tied to the fact that the "onClick" function is only able to be triggered when one input gains the "checked" attribute and another loses it, but the "defaultChecked" attribute does not count as an input being "fully checked" or something like that.
I could leave it at that, but the project that I am working on required me to have a default checked input radio button on the first-page render. So, I can't just delete the "defaultChecked" attribute and call it a day.
Any ideas on what could be causing this behavior?
UPDATE1
The following is the body of the inputclick() function:
//* Handle input selection
const inputClick = (attribute, event) => {
//* Parse state
let state = JSON.parse(JSON.stringify(itemState));
//* Get all the required values from the clicked input
const attributeId = attribute.id;
const itemTargetValue = event.target.value;
//* Check if the attribute is in state
const attributeIs = state.some(item => item.id === attributeId);
//* If the attribute does not exsist - add the attribute to state
if(attributeIs === false) {
const obj = {
id: attributeId,
selectedItem: itemTargetValue
};
state.push(obj);
return setitemState(state);
}
//* If the attribute id already exsists in state
if(attributeIs) {
//* Find the index of the attribute in question
const attributeIndex = state.map(object => object.id).indexOf(attributeId);
const attributeInQuestion = state[attributeIndex].selectedItem;
//* If the attribute's item's id is the same as the seelected input - do nothing
if(attributeInQuestion === itemTargetValue) {
return
}
//* If the attribute's item's id is not the same - change it to the new value
if(attributeInQuestion !== itemTargetValue) {
state[attributeIndex].selectedItem = itemTargetValue;
console.log(state);
return setitemState(state);
}
}
};
Here is the working code that fixes the issue.
Yes, there is some streamlining, for example, code shortening, etc. Yet, the difference that solves the issue is that...
The code that I had posted in the question originally was working. Meaning, that it was firing the inputClick() function and changing which input was selected, the problem was that...
...the defaultChecked logic in the was preventing the chosen input from being rendered as a selected input a.k.a. to change its CSS styling.
Bellow is the new onClick() function.
//* Handle input selection
const inputClick = (product, attribute, event) => {
let newCart = JSON.parse(JSON.stringify(cartState));
const productId = product.id;
const attributeId = attribute.id;
const itemTargetValue = event.target.value;
//* Find the product in cart state */
const productIndex = newCart.map((object) => object.id).indexOf(productId);
//* Find the attribute by id in question */
const attributeIndex = newCart[productIndex].selectedAttributes.map(object => object.id).indexOf(attributeId);
//* Change the products selected attribute item */
newCart[productIndex].selectedAttributes[attributeIndex].selectedItem = itemTargetValue;
setcartState(newCart);
};
Below is what the "inside" of the looks like now.
<input
type='radio'
key={product.id + attribute.name + item.id}
id={product.id + attribute.name + item.id}
name={product.id + attribute.name}
value={item.value}
defaultChecked={product.selectedAttributes[selectedId].selectedItem === item.value}
onChange={(event) => inputClick(product, attribute, event)}
>
</input>

Office UI Fabric Checkbox - How to Uncheck multiple check boxes at once through code

I render multiple checkboxes for a filter overlay. Now i want to clear all the checkboxes on a click of a button. How can i do this?
I render the checkboxes like this:
{
filter.Choices.map((choice: any) => (
<Checkbox
title = {filter.InternalName}
label = {choice}
onChange = {this._makeChangeHandler(choice, filter.InternalName, filter.Id)}
/>
))
}
Here is how i render my clear filter button:
<DefaultButton className={style.overlayClearButton} onClick={this._clearFilters}>Clear Filters</DefaultButton>
I want to uncheck all the checkboxes when i click the "Clear Filters" button.
you can use a controlled Checkbox for this. It´s basically the same, but you control the value of your checkbox on your own. Store this with your filter.Choices objects
filter.Choices.map((choice: any) => (
<Checkbox
title = {filter.InternalName}
label = {choice.label}
onChange = {this._makeChangeHandler(choice, filter.InternalName, filter.Id)}
checked={choice.isChecked}
/>
))
And on your button click you can now unset all checkboxes:
//Clear isChecked values of choices and set state for rerender
public _clearFilters() {
this.setState(state => ({...state, filter: state.filter.Choices.map(choice => {choice.isChecked = false; return choice;}))});
}
The solution depends on how you handle state in your application. But the best way is to control the checkbox state yourself.
I hope this helps,
greetings joe
for more sharepoint related help look at https://www.smarterbusiness.at/smarter-blog

How to access/reference component values inside a component

Trying to build a react component where I need to control checked status of checboxes and select options when change event occurs. But I don't know how it is possible to get value of the checked checkbox(es) and set the state.
We're using custom data-binding. On page load, we're assigning selected value of the select, with jQuery.
Programmatically changing value of the select must update matching check-boxes.
When user checks/unchecks a checkbox, corresponding value must be toggled on the select.
With jQuery I would loop trough check-boxes and build array with checked values then assign this value to the select on checkbox change. And when select change event is triggered, I would uncheck all check-boxes and check the ones matching selected items.
This is my simplified code.
state = {
items: [
{Key: 1, Value: "A"},
{Key: 29, Value: "Z"}
],
selected: [1, 29]
}
function onSelectChange(){
// Update checked checkboxes
}
function onCheckboxChange(){
// Update selected options
}
<div>
<select multiple onChange={onSelectChange} className="hidden">
{this.state.items.map((item, i) =>
<option value={item.Key}>{item.Value}</option>
)}
</select>
<div className="checkboxes">
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange} />
)}
</div>
</div>
You would use this.setState({}) inside the event handler to update the state of a component in React. This triggers a rerender in React which allows you to query the the updated state (this.state.selected).
Be advised that this.setState() expects an immutable object, so you should never change the previous, but always set a new state object!
Answer to comment:
For selectItem:
onSelectChange = event => this.setState({selected:event.target.value})
and for checkboxes (note the prevState):
onCheckboxChange = item => event => this.setState(({selected,...prevState})=> ({
...prevState,
selected: event.target.checked? selected.concat(item): selected.filter(it=> it!== item)
}))
and usage:
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange(item)} />
)}
This has the downside that it will create a new function on each rerender, so it's better to create a custom CheckboxItem and pass the item to it and use a handleClick.
onChange function give event where you could check whether the select box is being checked or not using this you can update your state accordingly.
function onCheckboxChange(e){
console.log("checked", e.target.checked);
// Then, on the basis of boolean you can update your state
}

Material-UI + React: Why doesn't <Table/>'s onRowSelection work for Select All?

Using Material-UI's <Table/> (http://www.material-ui.com/#/components/table) with ReactJS, I have a table set up with a select all checkbox. Every time a checkbox for a row is clicked on individually, the row id gets adds to the state array clickedRowIds. So console.log(), prints out an array with the ids of the clicked on row/s.
For example, if I were to check off the checkboxes just for the first row and second, the console log would print: ____THESE ARE THE CLICKED ROWS____ [1, 2] (With 1 and 2 representing the row ID numbers in integer). But when I click on the Select All checkbox in the header, it checks off all the checkboxes in the table, yet in the console.log(), it just shows ____THESE ARE THE CLICKED ROWS____ all and ids.forEach() gets an error Uncaught TypeError: ids.forEach is not a function.
The row checkboxes are all being clicked on via Select All checkbox, but why aren't all the row ID numbers being added to the clickedRowIds array? Using ids.forEach(), I would like to console log all the selected rows using Select All button.
export default class TestTable extends Component {
constructor(props) {
super(props)
this.state = {
clickedRowIds: [],
}
this.handleRowSelection = this.handleRowSelection.bind(this);
}
handleRowSelection(rowIds) {
this.setState({
clickedRowIds: rowIds
})
}
render(){
const ids = this.state.clickedRowIds
console.log('____THESE ARE THE CLICKED ROWS____ ', ids)
ids.forEach(id => {console.log(id)})
return(
<div className='table_body' style={styles.content}>
<Table
multiSelectable={true}
onRowSelection={this.handleRowSelection}
>
<TableHeader
displaySelectAll={true}
enableSelectAll={true}
>
...
</TableHeader>
</Table>
</div>
)
}
}
Answer will be upvoted and answered.
Check the docs here: http://www.material-ui.com/#/components/table
Called when a row is selected. selectedRows is an array of all row
selections. IF all rows have been selected, the string "all" will be
returned instead to indicate that all rows have been selected.
This means that if all rows are selected, annoyingly, your rowIds will no longer be set to an array, but a string saying "all". You'll need some logic to handle that case before calling forEach on rowIds. Your error is due to the string "all" not having the forEach method available.

Categories