react js update value from multiple radio and text box - javascript

I have an issue of updating state values of a particular key. Using multiple radio button and textbox.
Here is my state
this.state = {
PStudent:[{"flag_sts":1,"id":8472229,"remark":null,"status":"P","studentid":"12010013"},
{"flag_sts":1,"id":8472218,"remark":null,"status":"P","studentid":"12108051"},
{"flag_sts":1,"id":8472219,"remark":null,"status":"P","studentid":"12108052"}
],
};
On change value on radio:
const handleChange = (e,studentid) =>{
this.setState({
data: this.state.PStudent.map(item=>{
if (item.studentid !== e.target.name) {
return item;
}else{
return{
studentid: studentid,
status : e.target.value
}
}
})
})
}
And this is sending parameter form radio:
{(Object.keys(this.state.PStudent).length > 0) ? (
this.state.PStudent.map((v)=>(
<tr>
<td>{v.studentid}</td>
<td><input type="radio" name={v.studentid} value="P" onChange={(e)=>handleChange(e,v.studentid)} defaultChecked={(v.status == "P") ? true:false} /> </td>
<td><input type="radio" name={v.studentid} value="A" onChange={(e)=>handleChange(e,v.studentid)} defaultChecked={(v.status == "A") ? true:false} /> </td>
<td><input type="text" name="remarks" value="" /> </td>
</tr>
))
) : ''}
Would you like to help me how to update some value of particular key? In this case i would like to update value from key 'status' by radio button and key 'remarks' by text box. And object from PStudent will auto updated with the new value after do handleChange() by radio.
Thank you for your consider.

You may want to make use of the dynamic key and index here.
The dynamic key would allow you to reuse the same function for the value change.
The index can be used to identify the object's index in the array.
const handleChange = (e, index, field) =>{
const newPStudent = _.cloneDeep(this.state.PStudent); // or you can use this.state.PStudent.map(i => I);
newPStudent[index][field] = e.target.value
this.setState({PStudent: newPStudent})
}
{(Object.keys(this.state.PStudent).length > 0) ? (
this.state.PStudent.map((v, index)=>(
<tr>
<td>{v.studentid}</td>
<td><input type="radio" name={v.studentid} value="P" onChange={(e)=>handleChange(e, index, 'status')} defaultChecked={(v.status == "P") ? true:false} /> </td>
<td><input type="radio" name={v.studentid} value="A" onChange={(e)=>handleChange(e, index, 'status')} defaultChecked={(v.status == "A") ? true:false} /> </td>
<td><input type="text" name="remarks" value="" onChange={(e)=>handleChange(e, index, 'remark')}/> </td>
</tr>
))
) : ''}
If you are using underscore.js in your project, it's best to use _.cloneDeep() as it creates an independent copy of the object.

You can use functional version of setState as:
Live Demo
handleChange = (e, studentid) => {
const status = e.target.value;
this.setState((state) => {
return {
PStudent: state.PStudent.map((item) => {
if (item.studentid !== e.target.name) return item;
else return { ...item, status };
})
};
});
};

Related

How to make radio button checked/active depending on current state value

so I've got a header that cycles through 4 images every 5 seconds. The user can also cycle through these images themselves using 5 radio buttons.
The styling for the active/checked radio button is applied when the user clicks it themselves, however, if the timer switches the image the active radio button remains unchanged.
I know this is the expected behaviour based on my code below, I'm just wondering how I would go about changing the checked radio button to match it's current image!
Any help would be appreciated!
constructor(props) {
super(props);
this.state = {
time: 0,
start: 0,
currentIndex: 0
};
this.setIndex = this.setIndex.bind(this);
this.startTimer = this.startTimer.bind(this);
this.resetTimer = this.resetTimer.bind(this);
}
componentDidMount() {
this.startTimer();
}
componentDidUpdate() {
if (this.state.time === 5) {
this.setIndex(this.state.currentIndex + 1);
}
}
startTimer() {
this.timer = setInterval(
() =>
this.setState({
time: this.state.time + 1
}),
1000
);
}
resetTimer() {
this.setState({ time: 0 });
}
setIndex(index, e) {
console.log("changing");
if (index < 0) {
index = headers.length - 1;
}
if (index >= headers.length) {
index = 0;
}
this.setState({
currentIndex: index,
time: Date.now() - this.state.start
});
this.resetTimer();
}
... skipped code
<div className="carousel">
<img
alt="carousel-header"
className="background-img"
src={headers[this.state.currentIndex].image}
/>
<div className="buttons-container">
<input
type="radio"
value="1"
defaultChecked
name="index"
onClick={() => this.setIndex(0)}
></input>
<input
type="radio"
value="2"
name="index"
onClick={() => this.setIndex(1)}
></input>
<input
type="radio"
value="3"
name="index"
onClick={() => this.setIndex(2)}
></input>
<input
type="radio"
value="4"
name="index"
onClick={() => this.setIndex(3)}
></input>
</div>
</div>
You can check currentIndex for automatic change radio buttons.
In render function
const { currentIndex } = this.state
<div className="buttons-container">
<input
type="radio"
value="1"
defaultChecked
name="index"
checked={currentIndex === 0}
onClick={() => this.setIndex(0)}
></input>
<input
type="radio"
value="2"
name="index"
checked={currentIndex === 1}
onClick={() => this.setIndex(1)}
></input>
<input
type="radio"
value="3"
name="index"
checked={currentIndex === 2}
onClick={() => this.setIndex(2)}
></input>
<input
type="radio"
value="4"
name="index"
checked={currentIndex === 3}
onClick={() => this.setIndex(3)}
></input>
</div>
If I understood correctly, you are using radio buttons which are getting activated onclick (which is default HTML behavior) and are not when using your timer.
It is piece of cake, you just need to activate them via JS each time the timer switches background.
You probably assumed that your setIndex() function will run everytime your timer makes a change, but that is not a case, since that function only launches when person physically clicks on radio, or onClick event is emitted.
Because of:
onClick={() => this.setIndex(3)}
To fix it you should modify this piece of code:
startTimer() {
this.timer = setInterval(
() =>
this.setState({
time: this.state.time + 1
}),
1000
);
}
resetTimer() {
this.setState({ time: 0 });
}
There you should set the value of radio box, and update the state of index.
When resetting remember to reset the state value function and set value of radio box to #1 radio.

How to receive value as each separate object javascript

I'm trying to receive value as separate object array for each element.
This is my code
<input class="name" name="name[]" type="text" />
<input class="date_of_birth" name="DoB[]" type="text" />
<input class="age" type="radio" name="age" value="1" />
<input class="age" type="radio" name="age" value="0" />
var kidName = [];
$(".name").each(function() {
kidName.push($(this).val());
});
var newDob = [];
$(".date_of_birth").each(function() {
newDob.push($(this).val());
});
var age = [];
$(".age:checked").each(function() {
age.push($(this).val());
});
var kids = {
"kidName": kidName,
"newDob": newDob,
"age": age
};
How i get the value with this as separate array for each element.
kids: {
kidName: ["Test1", "Test2"]
newDob: ["20/02/2000", "20/03/2018"]
age: ["19", "1"]
}
But i want to receive these values like this
kids:
{
kidName: ["Test1"],
newDob: ["20/02/2000"],
age: ["19"]
},
{
kidName: ["Test2"],
newDob: ["20/03/2018"],
age: ["1"]
}
How can i achieve this, what changes should i make to receive values like this?
Thanks
One option is to put the form group into a container. Select the container and use map to loop thru the containers. In this example, the container is a div with class input-group
Note: you need to change the name of radio button every container.
var result = $(".input-group").map(function() {
return {
kidName: $(this).find('.name').val(),
newDob: $(this).find('.date_of_birth').val(),
age: $(this).find('.age:checked').val(),
}
}).get();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="input-group">
<input class="name" name="name[]" type="text" value="Test1" />
<input class="date_of_birth" name="DoB[]" type="text" value="20/02/2000" />
<input class="age" type="radio" name="age01[]" value="1" checked/>
<input class="age" type="radio" name="age01[]" value="0" />
</div>
<div class="input-group">
<input class="name" name="name[]" type="text" value="Test2" />
<input class="date_of_birth" name="DoB[]" type="text" value="20/03/2018" />
<input class="age" type="radio" name="age02[]" value="1" />
<input class="age" type="radio" name="age02[]" value="0" checked/>
</div>
Based on what is present in your question, the best I can suggest is just to reformat the data you have into the format you need.
Id you're able to provide your html, it may be possible to get the data into this format initially rather than reparsing it.
const kids = {
kidName: ["Test1", "Test2"],
newDob: ["20/02/2000", "20/03/2018"],
age: ["19", "1"],
};
const kidsArraysToObjects = kids => kids.kidName.map(
(_, i) => Object.keys(kids).reduce((prev, curr) => ({
...prev,
[curr]: kids[curr][i]
}), {})
)
const result = kidsArraysToObjects(kids)
console.dir(result)
Here is what i have tried
var kids = []
$(".name").each(function(index, value) {
if (kids[index] == undefined) {
kids[index] = {}
}
kids[index].kidName = $(this).val()
});
$(".date_of_birth").each(function(index, value) {
if (kids[index] == undefined) {
kids[index] = obj
}
kids[index].newDob = $(this).val()
});
$(".age:checked").each(function(index, value) {
if (kids[index] == undefined) {
kids[index] = obj
}
kids[index].age = $(this).val()
});
NOTE: I haven't tested this
try this replace parent-selector-each-record with common class or id of each row
by using this approach you get exact data. also you can use #OliverRadini approach
var kids = [];
$("parent-selector-each-record").each(function() {
var kid = {};
kid["kidName"] = $(this).find(".name").val()
kid["newDob"] = $(this).find(".date_of_birth").val()
kid["age"] = $(this).find(".age:checked").val()
kids.push(kid);
});
I also suggest to reformat your object structure after that you get the data, since it will avoid some coupling with your HTML. In other words, it avoids to touch your JavaScript when you want to change your HTML.
const kids = {
kidName: ["Test1", "Test2"],
newDob: ["20/02/2000", "20/03/2018"],
age: ["19", "1"]
}
const keys = Object.keys(kids);
const result = kids[keys[0]].map((_, i) => {
return keys.reduce((obj, key) => {
obj[key] = kids[key][i];
return obj;
}, {});
});
console.log(result);

html5 input type range always pointing to min value and not getting onChange on text input

I am trying to use input type text along with input type range where three usecases are there.
1. defaultcase: when page is loaded the slider should point to the value as given in the text input field.
2. when user enters the text value, the slider pointer should also update.
3. when user uses the slider to change value, the text value should get updated.
In my below code
In default case: in input type range when i give defaultValue="200" the slider pointer points always to min value.
When i enter value in text area, the slider is not getting updated.
i tried some ways to fix it but failing.
if (
Type == "integer" &&
LowerBound &&
UpperBound !== 0
) {
editComponent = (
<div className="SettingsTreeValueNode__InputField">
<input
type="text"
pattern="[0-9]*"
ref="text"
value={this.state.value}
oninput={this.handleinputChanged.bind(this)}
className="inputtext"
onBlur={e => this.focusOfInputLost(e)}
/>
{LowerBound}
<input
type="range"
className="sliderinput"
defaultValue="200"
min={LowerBound}
max={UpperBound}
step="1"
oninput={this.handleSliderChange.bind(this)}
onMouseUp={this.handleWhenMouseReleased.bind(this)}
/>
{UpperBound}
</div>
);
}
handleSliderChange(event) {
var value = event.target.value;
var slidervalue = parseInt(value);
this.setState({ value: slidervalue });
}
handleInputChanged(event) {
var value = event.target.validity.valid
? event.target.value
: this.state.value;
this.setState(
{
value: value,
inputValid: this.isInputValid()
});
}
ยดยดยด[![slider along with text area][1]][1]
[1]: https://i.stack.imgur.com/w5MZ6.png
Below code will solve your problems, only use single state object to display value in both controls now it will works in both way either change value on input or slider.
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
}
handleSliderChange = (event) => {
var value = event.target.value;
var slidervalue = parseInt(value);
this.setState({ value: slidervalue });
}
handleInputChanged = (event) => {
var value = event.target.validity.valid
? event.target.value
: this.state.value;
this.setState(
{
value: value
});
}
render() {
let LowerBound = 0;
let UpperBound = 200
return (
<div>
<button onClick={this.handleCopy}>click me</button>
<div className="SettingsTreeValueNode__InputField">
<input
type="text"
pattern="[0-9]*"
ref="text"
value={this.state.value}
onInput={this.handleInputChanged}
className="inputtext"
/>
{LowerBound}
<input
type="range"
className="sliderinput"
defaultValue="200"
min="0"
max="200"
step="1"
value={this.state.value ? this.state.value : UpperBound}
onInput={this.handleSliderChange}
/>
{UpperBound}
</div>
</div>
);
}
}
export default App;

How to organize multiple checkboxes?

I want to organize multiple checkboxes. I decided to keep an array of ids of chosen boxes in state.And using method want to give them statuses "checked".
constructor(props) {
super(props);
this.state = {
checkboxes: []
}
}
for(let i=0; i<checkboxData.length; i++) {
checkboxes.push(
<List.Item key = { checkboxData[i].id }>
<div className="ui checkbox">
<input type="radio"
id={checkboxData[i].id}
checked={ ()=>this.getCheckboxStatus(checkboxData[i].id)}
onChange = { this.setTag }
/>
<label>{checkboxData[i].name}</label>
</div>
<List.Content floated="right" >
<Label>
0
</Label>
</List.Content>
</List.Item>
);
}
getCheckboxStatus = checkBoxId => {
let { checkboxes } =this.props;
console.log('IDS', checkBoxId)
if (checkboxes.length === 0) {
return false;
} else if (checkboxes.indexOf(checkBoxId)!==-1){
return true;
}
return false;
};
setTag = e => {
let { checkboxes } = this.state;
if ( checkboxes.length === 0 ) {
checkboxes.push( e.target.id );
} else {
if(checkboxes.indexOf(e.target.id)!==-1) {
checkboxes.splice(checkboxes.indexOf(e.target.id),1);
} else {
checkboxes.push( e.target.id );
}
}
this.setState({ checkboxes: checkboxes })
}
React renders it and throws in console:
Warning: Invalid value for prop `checked` on <input> tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.
How I understand this is because I'm using method for checked attribute. How can I do same thing and not receive warning?
Your input type needs to be checkbox, not radio. You also need to reference state with your checked attribute.
<input
type="checkbox"
id={checkboxData[i].id}
checked={this.state.checkBoxes[i]}
onChange={this.setTag}
/>
This will set state with true or false depending on the state of the checkbox

Focus on next field when pressing enter React.js

I would like to find a way to focus on the next field when I click enter in the input using React.js
#autobind
handleKeyPress(event){
if(event.key === 'Enter'){
this.refs.email.focus();
}
}
#autobind
handleKeyPressEmail(event){
if(event.key === 'Enter'){
this.refs.zip_code.focus();
}
}
<input
onKeyPress={this.handleKeyPress}
ref = 'name'
/>
<input
onKeyPress={this.handleKeyPressEmail}
ref = 'email'
/>
<input
ref = 'zip_code'
/>
This is the best way I have found so far, however I don't want to repeat myself by creating a function everytime I want that to happen. Is there a better and cleaner way to implement this?
If <form> is present:
function handleEnter(event) {
if (event.keyCode === 13) {
const form = event.target.form;
const index = Array.prototype.indexOf.call(form, event.target);
form.elements[index + 1].focus();
event.preventDefault();
}
}
...
<form>
<input onKeyDown={handleEnter} />
<input onKeyDown={handleEnter} />
<input />
</form>
CodePen
Without <form>:
function useFocusNext() {
const controls = useRef([]);
const handler = (event) => {
if (event.keyCode === 13) {
// Required if the controls can be reordered
controls.current = controls.current
.filter((control) => document.body.contains(control))
.sort((a, b) =>
a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
? -1 : 1
);
const index = controls.current.indexOf(event.target);
const next = controls.current[index + 1];
next && next.focus();
// IE 9, 10
event.preventDefault();
}
};
return useCallback((element) => {
if (element && !controls.current.includes(element)) {
controls.current.push(element);
element.addEventListener('keydown', handler);
}
}, []);
};
...
const focusNextRef = useFocusNext();
<input ref={focusNextRef} />
<input ref={focusNextRef} />
<button ref={focusNextRef}>Submit</button>
CodePen
You can use componentDidMount and auto bind refs through a for-in loop.
http://codepen.io/jzmmm/pen/PzZgRX?editors=0010
constructor() {
super();
this._handleKeyPress = this._handleKeyPress.bind(this);
}
// Loop through the ref's object, and bind each of them to onkeypress
componentDidMount() {
for (let x in this.refs) {
this.refs[x].onkeypress = (e) =>
this._handleKeyPress(e, this.refs[x]);
}
}
// This checks ENTER key (13), then checks if next node is an INPUT
// Then focuses next input box
_handleKeyPress(e, field) {
if (e.keyCode === 13) {
e.preventDefault(); // Prevent form submission if button present
let next = this.refs[field.name].nextSibling;
if (next && next.tagName === "INPUT") {
this.refs[field.name].nextSibling.focus();
}
}
}
render() {
return (
<form>
<input type="text" name="name" ref='name' />
<input type="text" name="email" ref='email' />
<input type="text" name="zip_code" ref='zip_code' />
</form>
);
}
Without <form> and TypeScript version.
Skip disabled inputs.
const onKeyPress: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
if (e.key === "Enter") {
const inputs = Array.from(
// Get table or tbody whatever that contains all inputs. The number of parentElements depends on the structure of your html
e.currentTarget?.parentElement?.parentElement?.parentElement?.querySelectorAll(
"input"
) ?? []
).filter((e) => !e.disabled)
const index = inputs.indexOf(e.currentTarget)
inputs[index + 1]?.focus()
e.preventDefault()
}
},
[]
)
return <input type="number" onKeyPress={onKeyPress} />
This is how I managed to make it simpler:
#autobind
handleKeyPress(value, event){
if(event.key === 'Enter'){
this.refs[event].focus();
}
}
<input
onKeyPress={(event) => this.handleKeyPress('email', event)}
ref = 'name'
/>
<input
onKeyPress={(event) => this.handleKeyPress('zip_code', event)}
ref = 'email'
/>
<input
ref = 'zip_code'
/>

Categories

Resources