Handle multiple inputs - javascript

This task is tough one for me. Please don't get mad. I'm making dynamic inputs to able for user add more informartion.Expected output if both field was filled, push it to the array. I have one datalist and one input. For both of them I want to use one onChange function to make validation and push to the array. Right now, in onChange function I'm checking datalist, does value was typed or selected from options. The problem is that when I'm storing in state, it only saves one sigle value or doesn't push to array. I commented where is the problem in the code. I have tried to reproduced my code : https://jsfiddle.net/armakarma/qwg3j2fa/9/
{this.state.arrayOfInput.map((item, idx) => {
return (
<>
<input
type="text"
name="address"
list="data-list"
onChange={e => this.onChooseAddress(e)}
/>
<datalist id="data-list">
{this.state.adresses.map((item, idx) => {
const { id, address } = item
return <option key={idx} data-value={id} value={address} />
})}
</datalist>
<input
name="contact"
className="event_time-inputs event_table_textarea"
placeholder="Contact"
onChange={e => this.onChooseAddress(e)}
/>
</>
)
})}
onChange function:
onChooseAddress(e) {
const { adresses } = this.state
let address_id = ""
let new_address = ""
let whoIsMetting = ""
// checking if value from "contact" input
if (e.target.name === "contact") {
whoIsMetting = e.target.value
}
// checking if value is selected from options in datalist
for (let i = 0; i < adresses.length; i++) {
if (
e.target.value === adresses[i].address &&
e.target.name === "address"
) {
address_id = adresses[i].id
}
}
//if user typed value
if (!address_id && e.target.name === "address") {
new_address = e.target.value
}
// PROBLEM IS HERE!!!! if both fields was filled pushing to array
if ((address_id || new_address) && whoIsMetting) {
let interviewDetails = {
address_id: address_id,
new_address: new_address,
whoIsMetting: whoIsMetting,
}
this.setState(prevState => ({
arrayOfAddresses: [...prevState.arrayOfAddresses, interviewDetails],
}))
}
}

Your problem is, that only one of the three variables address_id, new_address and whoIsMetting can have a value at a call of onChooseAddress.
You can never have a value in whoisMetting and one of the others together, because whoIsMetting only gets a value if onChooseAddress is called from the contact input and the others if it is called from the address input.
I suggest you either stash partly correct values in a different variable (outside of the method ofc) and check for completion before pushing them to the arrayOfAddresses OR simply push partly correct entries directly to arrayOfAddresses and check for completion before using an entry of this array.

Related

How to get object data when one value is changed in React?

In a React project, I've certain records of data which are populated in a grid. It also has two input fields one for text and other is date. User can make any changes on text or date field. My concern is that when date value is changed, text data is shown in console along with new date value, but, when text value or date value from other record is changed, the previous record's text value is reset to old value. Please refer to the code below
Below is the code where I'm extracting both text and date values on handlechange
const handleChange = (data) => {
const { id: newId, textVal: franchise, dateVal: dateData } = data;
setDataNew((prevInfo) => {
const newList = [...prevInfo];
const index = newList.findIndex((datum) => datum.newId === newId);
if (index !== -1) {
if (newId !== undefined) {
newList[index].newId = newId;
}
if (franchise !== undefined) {
newList[index].franchise = franchise;
}
if (dateData !== undefined) {
newList[index].dateData = dateData;
}
} else {
newList.push({ newId, franchise, dateData });
}
return [...newList];
});
};
These are the changes
<input type="text" onChange={(e) => textCallback({textVal: e.target.value,id: rowData[0].id,
dateVal: rowData[4] <-- This date value is added in text field
})}/>
<DatePickerNew setRequesterDate={(e) => dateCallback({dateVal: e, id: rowData[0].id,
textVal: rowData[3] <-- This text value is added in date field
})} startDate={colData}
/>
As you can see from above code, new keys have been added to get the values of respective fields. It works for date field but, not for text field. What could be the best optimal solution?
Please refer to codesandbox link --> https://codesandbox.io/s/keen-dubinsky-7z53vc?file=/src/Grid.js
Do observe the console output to see the changes

how to show pincodes in sorted order on blur in react?

I am using react-hook-form.I have pincode textarea in which user can enter pincodes,Now I want to validate my value on blur mean pincode should be 6digit and present in sorted order.
onblur event not work properly ?
here is my code
https://codesandbox.io/s/react-hook-form-get-started-v24jk
<TextField
inputRef={register}
label={"pincode"}
variant="outlined"
placeholder={"dd"}
multiline={true}
rows="4"
rowsMax="12"
name={"pincode"}
onBlur={v => {
console.log(v);
function onlyUnique(value, index, self) {
return self.indexOf(value) === index && value.trim().length === 6;
}
if (v) {
let codes = v.split(",");
let newCodes = codes.filter(onlyUnique);
return newCodes.sort().join(",");
}
return "";
}}
/>
react hook form API
https://react-hook-form.com/api
onBlur={v => {...}
Your v variable represents the event object for the onBlur event. You can get the value from the textarea from v.target.value. v.target.value.split(",") should work, though you may end up with an empty string in your array if there's only one PIN entered.

Checkbox checked with js map

I am implementing some kind of survey, and creating questions and choices based on api response, using map to handle checkbox onChange, it works very well. However, I am unable to make checkbox checked properly.
Here is checkbox
<FormControlLabel
key={choice.choiceID}
value={choice.choiceID}
control={<Radio />}
label={choice.text}
labelPlacement="end"
name={"" + question.questionID}
onChange={handleChange}
checked={
qValues1.get(question.questionID + "") === question.questionID + ""}
/>
I am converting questionID to string, because name prop should be string.
function handleChange(event) {
const { name, value } = event.target;
qValues1.set(name, value); // adding into map, which is state
if (qValues1.get(name) === value) {
return true;
}
}
I tried converting map into array, and trying to find choiceID using .includes(), but no luck.
{qValues1.get(question.questionID + "") === question.questionID + ""}
You're checking if the map qValues contains a key matching its value. So you should do :
const { name, checked } = event.target
qValues1.set(name, checked ? name : undefined);
in handleChange.
It looks like you're using Material-UI, so you might want to mention that. Sometimes they change the props and stuff so it can get a little wonky. As far as your code goes it looks like your handleChange() function may be the issue. Every time I set up an onChange event I make sure to use the syntax event.target.value to capture the inputs. I'm not sure if that works but give it a try! I've never seen a de-structured syntax like that before in your code -- const { name, value } = event.target; so that's throwing me through a loop. What are your errors looking like?
So, it will like below now, works just fine.
<FormControlLabel
key={choice.choiceID}
control={<Radio color="primary" />}
label={choice.text}
labelPlacement="end"
name={question.questionID + ""}
value={choice.choiceID + ""}
/>
and handleChange
const { name, value } = event.target;
let { checked } = event.target;
qValues1.set(name, checked ? value : undefined);

need something like event.target.focus() in React

I'm creating a dynamic list component that basically renders a new empty text field every time something is added to the list. The component is as follows (it uses a custom TextFieldGroup but I don't think that code is necessary as nothing out of the ordinary is going on.):
const DynamicInputList = ({ inputs, onChangeArray, label, name, errors }) =>
{
const nonEmptyInputs = inputs.filter(input => {
return input !== "";
});
const lastIndex = nonEmptyInputs.length;
return (
<div>
{nonEmptyInputs.map((input, index) => {
return (
<Grid container spacing={16} key={index}>
<Grid item xs={12}>
<TextFieldGroup
label={label}
id={`${name}${index}`}
name={name}
value={input}
onChange={e => onChangeArray(e, index)}
errors={errors[name]}
/>
</Grid>
</Grid>
);
})}
<Grid container spacing={16} key={lastIndex}>
<Grid item xs={12}>
<TextFieldGroup
label={label}
id={`${name}${lastIndex}`}
name={name}
value={inputs[lastIndex]}
onChange={e => onChangeArray(e, lastIndex)}
errors={errors[name]}
/>
</Grid>
</Grid>
</div>
);
};
The onChangeArray function is as follows:
onChangeArray = (e, index) => {
const state = this.state[e.target.name];
if (e.target.value === "") {
state.splice(index, 1);
this.setState({ [e.target.name]: state });
} else {
state.splice(index, 1, e.target.value);
this.setState({ [e.target.name]: state });
}
};
Everything works fine except when a blank field is changed (begin to type) it immediately removes focus from any of the fields. I'm looking for a way to keep the focus on this field while still adding the new one below.
Thanks in advance.
The problem is that you're encountering the following sequence of events (assuming inputs length of 5):
React renders 1 field (key = 5)
User starts typing 'a'
Your state change is triggered
React renders 2 entirely new fields (key = 1 & value = 'a', key = 5) and trashes the old one (key = 5)
There are at least two solutions
Don't delete / trigger re-render of the original field
This is the cleaner solution.
First, you are directly mutating state in onChangeArray with your use of slice, because that does an in-place modification. Perhaps this isn't causing problems now, but it's a big React anti-pattern and could create unpredictable behavior.
Here's a quick fix:
onChangeArray = (e, index) => {
const state = [...this.state[e.target.name]];
if (e.target.value === "") {
state.splice(index, 1);
this.setState({ [e.target.name]: state });
} else {
state.splice(index, 1, e.target.value);
this.setState({ [e.target.name]: state });
}
};
And other options
I'll leave the reworking of the implementation to you, but the gist would be:
Use consistent key references, which means iterate over all your inputs and just skip the empty ones instead of finding the empty ones first and setting that arbitrary key to the total length. Of course, you should still render the first empty one.
You could consider rendering everything, and use CSS display:none attribute as needed to accomplish your desired UX
Leverage input's autofocus attribute
I assume your TextFieldGroup uses an input element. You could pass the autofocus attribute to the field you want to have focus. This answers your original question.
But this isn't recommended, as you're still needlessly running through those re-render cycles and then putting a patch on top.

update 2nd item when the 1st item is the same - reactjs

My main state:
this.state = {
list: []
}
I have a form w/c takes 2 separate arguments: listItem and quantity
render () {
return (
<div>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit={this.handleAddOption}>
<input type="text" placeholder="description" name="description" /> <br/>
<input type="number" placeholder="quantity" name="quantity" /> <br/><br/>
<button>Add Grocery</button>
</form>
</div>
);
}
}
So basically the two forms are being submitted to the handleAddOption function:
handleAddOption(description, quantity){
if (!description && !quantity) {
return 'Enter valid description and quantity to add item';
} else if (this.state.list.indexOf(description) > -1) {
// must update the quantity
}
this.setState((prevState) => ({
list: prevState.list.concat(description, quantity)
}));
}
So basically the list is an array. There are two things I want to do here:
1. I need to add this two to my main array list state and render them side by side on the screen like:
item1 1
item2 2
As of the moment, I am only concatenating both as text w/c is not a good idea since I need to update the quantity later.
I want to find a way to update the quantity if the same description was placed to the to the form for instance:
1st Input:
item1 1
2nd Input:
item1 5
So it must update the item1 quantity to 5
Any idea how I can handle this?
In order to save the information to an array, we need to figure out the location of the description inside of the array.
You're using the concat function in your example. This function add the two items to the array, but as separate entries. I would recommend you to save the information to a dictionary, this combines the values into a single entry. In JavaScript, we can do this using objects:
prevState.list.push({
description: description,
quantity: quantity
});
In order to get the description from the object, we use the dot notation.
We would save an object to a variable, let's name it entry, and access the description like:
entry.description
In order to make this work, we are supposed to change the function a little bit. Since we can't just check for the presence of a value, we need to loop over the entries of the list. When we do, we have to check whether the descriptions match, if none does, we add the new entry
handleAddOption(description, quantity){
var hasBeenFound = false;
prevState.list.forEach(function (entry) {
if (entry.description === description) {
hasBeenFound = true;
entry.quantity = quantity;
}
});
if (!hasBeenFound) {
elements.push({description: description, quantity: quantity})
}
}
I hope my description made a little sense and that you are able to continue.
Your handleAddOption function edit like this.
handleAddOption = (e) => {
e.preventDefault();
let description = e.target[0].value;
let quantity = e.target[1].value;
if (!description && !quantity) {
return 'Enter valid description and quantity to add item';
} else {
const list = this.state.list.map((el, i) => {
if (el.indexOf(description) > -1) {
// must update the quantity
}
return (el)
});
}
this.setState((prevState, props) => ({
list: prevState.list.concat(description + ' ' + quantity)
}));
}

Categories

Resources