I have a small problem with my dynamic form. In the code below the render method I have code that maps an input and a dropdown select menu to fill in state.questions[{title: "", type: ""}]. You can see the addQuestionsHandler method to add more questions and the questionsInputHandler to handle the questions values.
The surveyInputHandler method handles the static questions in the return function.
The problem I'm having is that in my code for the dynamic questions the input value and the select dropdown value are ending ending up the same in state.questions[{title: "", type: ""}]. If I input "Test" - both title and type will be "Test". If I input "Test" and select value = "Radio Button" - both title and type will be "Radio Button". If I don't select a dropdown option value, then both will be the input value. If I do select a dropdown option value then the input value will be overridden by the dropdown select value.
I've racked my brain for a while but I need more eyes on it. Can you please let me know what I'm not doing correctly? Thanks so much.
const questionTypes = [
"Select Question Type",
"Textbox",
"Radio Button",
"Checkbox"
];
class SurveyQuestions extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
description: "",
pointsValue: 0,
questions: [
{
title: "",
type: ""
}
]
};
}
surveyInputHandler = e => {
console.log(e.target.value);
this.setState({
[e.target.name]: e.target.value,
[e.target.title]: e.target.value,
[e.target.description]: e.target.value,
[e.target.pointsValue]: e.target.value
});
};
questionsInputHandler = idx => e => {
console.log(e.target.value);
const newQuestions = this.state.questions.map((question, qidx) => {
if (idx !== qidx) return question;
return {
...question,
title: e.target.value,
type: e.target.value
};
});
this.setState({
questions: newQuestions
});
};
addQuestionHandler = () => {
this.setState(prevState => ({
questions: [...prevState.questions, { title: "", type: "" }]
}));
};
submitHandler = e => {
const { title, description, pointsValue, questions } = this.state;
console.log(
title,
description,
pointsValue,
questions.map(question => ({ ...question }))
);
this.setState({
title: "",
description: "",
pointsValue: "",
questions: [{ title: "", type: "" }]
});
e.preventDefault();
};
render() {
const { title, description, pointsValue, questions } = this.state;
const questionsDisplay = questions.map((question, idx) => (
<div key={idx} className="SurveyQuestions__QuestionContainer">
<h5>Question {idx + 1}</h5>
<label htmlFor="questionTitle">Question Title</label>
<input
type="text"
id="questionTitle"
placeholder={`Question Title #${idx + 1}`}
value={question.title}
onChange={this.questionsInputHandler(idx)}
/>
<label htmlFor="questionType">Type</label>
<select
className="SurveyQuestions__QuestionTypesDropdown"
value={question.type}
onChange={this.questionsInputHandler(idx)}
>
{questionTypes.map((type, tidx) => (
<option key={tidx} id={`${type}-${tidx}`} value={type}>
{type}
</option>
))}
</select>
</div>
));
Solved:
So the simple solution was to create a separate input handler for the select dropdown menu. The code is below:
questionTypeInputHandler = idx => e => {
const newQuestions = this.state.questions.map((question, qidx) => {
if (idx !== qidx) return question;
return {
...question,
type: e.target.value
};
});
this.setState({
questions: newQuestions
});
};
Related
I have an edit page of a recipe that is populated correctly using findOne axios call and populates all of the information for that recipe. However, it only displays the checkboxes that are checked and not the checkbox objects that are unchecked. I want it to display all checkboxes, both checked and unchecked. See below code.
Edit.jsx
const Edit = (props) => {
const { _id } = useParams({})
const [form, setForm] = useState({
title: "",
description: "",
tags: [
{ name: "Athletic/Higher caloric", isChecked: false },
{ name: "Aggressive Weight Loss", isChecked: false },
{ name: "Kid-Friendly", isChecked: false },
{ name: "Non-Vegan", isChecked: false },
{ name: "Diabetes reversal", isChecked: false },
{ name: "Quick and Easy", isChecked: false },
],
})
useEffect(() => {
axios.get(`http://localhost:8000/api/recipes/${_id}`)
.then(res => {
console.log(res.data.results);
setForm(res.data.results);
})
.catch(err => {
console.log(err)
setErrors(err)
})
}, [_id])
My checkedHandler from create page:
const handleCheckedTags = (index) => {
setForm(prev => ({
...prev,
tags: [
...prev?.tags?.map(
({ isChecked, ...rest }, idx) => (
idx === index ?
{ ...rest, isChecked: !isChecked } :
{ ...rest, isChecked })
)]
}));
}
And here is my return code (the edit form):
return (
<form>
{
form.tags.map((tag, i) => (
<div className="form-inline mx-3" key={i}>
<input
type="checkbox"
value={tag.name}
checked={tag.isChecked}
onChange={(event) => handleCheckedTags(i)}
key={i}
/>
</div>
))}
</div>
<input type="submit" />
</form>
I know I have a single form state to begin with, which was how I setup my create form. Should I have set it up differently or is there a way to make sure the unchecked checkboxes display for the edit page?
Thanks in advance for your help.
**Update**
With some help I was able to merge the results from the server (with the recipe info in it) into the original form by using map operators. See below.
useEffect(() => {
document.title = "NutritarianEats - Edit"
axios.get(`http://localhost:8000/api/recipes/${_id}`)
.then(res => {
const updatedTags = [...form.tags];
const result = res.data.results;
result.tags.map((tag, i) => {
updatedTags.map((existingTag) => {
if (existingTag.name === tag.name) {
existingTag.isChecked = tag.isChecked
}
})
})
setForm({ ...result, tags: updatedTags });
})
.catch(err => {
setErrors(err)
})
}, [_id])
I think I could have not made the tags an array initially setting up my model and instead made it one object. Something I need to work on, but this code worked for me.
I thinking for few days but cant realize how can i make it. I have 4 json data and 4 picker.
Its for city,district,village,neirborhood. In first i must choose city then in second picker it must show district about that i choose city. When i choose district from picker third one must show villages about that district. And neirborhood is same too. In that json datas they have some connection. Like city json have ' id-name' district have 'id-cityid-name' village have 'id-districtid-name' neirborhood have 'id-villageid-name' Like that. But i cant figure out how can i make it. Its my codes I really stuck with that hardly i need some help please. Thank you! My codes :
Elements :
const DropdownElements = [
{
key: 1,
title: "Şehir",
placeholder: "Şehir Seçiniz",
apiUrl: "https://api.npoint.io/995de746afde6410e3bd",
type: "city",
selecteditem: "",
data : [],
},
{
key: 2,
title: "İlçe",
placeholder: "İlçe Seçiniz",
apiUrl: "https://api.npoint.io/fc801dbd3fc23c2c1679",
type: "district",
selecteditem: "",
data : [],
},
{
key: 3,
title: "Köy",
placeholder: "Köy Seçiniz",
apiUrl: "https://api.npoint.io/72cf025083b70615b8bb",
type: "village",
selecteditem: "",
data : [],
},
{
key: 4,
title: 'Mahalle',
placeholder:'Mahalle Seçiniz',
apiUrl: 'https://api.npoint.io/0c04c63923c8ca4e117b',
type: 'neighborhood',
selecteditem: "",
data : [],
},
];
Component :
const PickerCompanent = (props) => {
const [xdata, setData] = useState([]);
const [newData, setNewData] = useState([]);
let x;
let y = [];
// data.filter((a) => a.il_id == "3");
useEffect(() => {
props.datasource.then(setData);
switch (props.type) {
case "city":
x = props.selecteditem;
setNewData(xdata);
break;
case "district":
y = xdata.filter((element) => {
if (props.selecteditem === element.id) {
return element;
}
});
break;
case "village":
console.log("village");
break;
default:
console.log("def");
break;
}
}, [props.datasource]);
return (
<Select
showSearch
style={{ width: 200, marginLeft: 15 }}
placeholder={props.placeholder}
optionFilterProp="children"
onChange={(x) => props.onChange(x)}
onFocus={props.onFocus()}
datasource={xdata}
onSearch={props.onSearch()}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{xdata &&
xdata.map((x) => {
return (
<Select.Option key={x.id} value={x.id}>
{x.name}{" "}
</Select.Option>
);
})}
</Select>
);
};
My App :
const App = () => {
const [dataap, setDataAp] = useState([]);
const [idhold, setIDHold] = useState();
const filldata = (value) => {};
function onChange(value) {
setIDHold(value);
console.log(value);
}
const getData = (value, type) => {
return fetch(value)
.then((x) => x.json())
.then((y) => {
return y;
});
};
function onFocus() {}
function onSearch(val) {}
return (
<Space>
{DropdownElements.map((x) => {
return (
<PickerCompanent
showSearch
selecteditem={idhold}
key={x.key}
placeholder={x.placeholder}
type={x.type}
datasource={getData(x.apiUrl)}
onFocus={onFocus}
onChange={(z) => onChange(z)}
onFocus={onFocus}
onSearch={onSearch}
></PickerCompanent>
);
})}
</Space>
);
};
If you need i can give my teamviewer or skype too. I really need that help thanks for replies!
Sandbox : codesandbox.io/s/runtime-monad-vxit
https://codesandbox.io/s/mystifying-moore-7w105?file=/src/App.js
Select CityTwo to see the dropdown update.
You need a switch. Updating arrays inside state is tricky. You can't populate or push anything in an array that's in state. Update your array outside state, THEN update state.
So in my application user have an option to enter a number, for example if user enters as "5" then it will add 5 textInputs. By this, i have successfully looped the textInput. Now, how to populate or store the values in the state ?
My code :
this.state =
{
usersDetails: [{name: "", age: "", gender: "", primary: false}, {name: "", age: "", gender: "", primary: false}]
};
handleChange(i, e) {
const { name, value } = e;
let users = [...this.state.usersDetails];
users[i] = {...users[i], [name]: value};
this.setState({ usersDetails });
console.log(this.state.usersDetails);
}
let items = [];
for (let i = 0; i < this.props.maxSeats; i++) {
items.push(
<TextInput
placeholder="Enter Name"
onChangeText={this.handleChange.bind(this, i)}
value={this.state.userDetails}
/>)
}
With the help of stockoverflow i created the handleChange function but it gives error ! How to populate the values and save on the state for dynamic form !
Kindly guide !
handleChange = (value, index,key) => {
this.setState((prevState) => ({
...prevState,
usersDetails: prevState.usersDetails.map((val, mapIndex) => {
if (mapIndex === index) {
val[key] = value;
return val;
}
return val;
}),
}));
};
for (let i = 0; i < this.props.maxSeats; i++) {
items.push(
<TextInput
key={i}
placeholder="Enter Name"
onChangeText={(text) => {
handleChange(text, i,'name'); // here you can pass name,age whatever
}}
value={this.state.userDetails[i].name}
/>,
);
}
My problem is when I'm deleting inputs that added dynamically it delete's wrong input. I reproduced my code in jsfiddle https://jsfiddle.net/armakarma/qwg3j2fa/24/ . Try to add five more inputs, type something in each input and try to delete second input. It will delete last one. Where I'm doing mistake?
addNewInputs() {
let newInputValues = {
datetime: "10.05.2019 14:00",
position_id: 1,
contact: "",
address_id: "",
new_address: "",
}
this.setState(prevState => ({
arrayOfAddresses: [...prevState.arrayOfAddresses, newInputValues],
}))
}
deleteInput(idx) {
let tempObj = this.state.arrayOfAddresses
tempObj.splice(idx, 1)
this.setState(prevState => ({
arrayOfAddresses: tempObj,
}))
}
onChooseAddress(e, idx) {
console.log(e.target.value)
}
render() {
return ( <
div > {
this.state.arrayOfAddresses.map((item, idx) => {
return (
<div key = {idx} >
<input name = "contact"
onChange = {(e) => this.onChooseAddress(e)}
/>
<button onClick = {() => this.deleteInput(idx)} > x < /button>
</div>
)
})
}
<button onClick = {() => this.addNewInputs()} > Add new input < /button>
/div>
)
}
}
The problem is with the chooseAddress method, you're not passing the index from the onChange callback, that's why the state is not updating, and also you have not added value prop to the input, that's why rendering was wrong, because of input's internal state
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
adresses:[
{
"id": 1,
"address": "address 1",
},
{
"id": 2,
"address": "address 2",
},
{
"id": 3,
"address": "address 3",
},
{
"id": 4,
"address": "address 4",
}
],
arrayOfAddresses: [
{
datetime: "10.05.2019 14:00",
position_id: 1,
contact: "",
address_id: "",
new_address: "",
},
],
}
}
addNewInputs() {
let newInputValues = {
datetime: "10.05.2019 14:00",
position_id: 1,
contact: "",
address_id: "",
new_address:"",
}
this.setState(prevState => ({
arrayOfAddresses: [...prevState.arrayOfAddresses, newInputValues],
}))
}
deleteInput(idx) {
this.setState(prevState => {
let tempObj = [...prevState.arrayOfAddresses]
tempObj.splice(idx, 1)
console.log(tempObj)
return {
arrayOfAddresses: tempObj,
}
})
}
onChooseAddress(e, idx) {
const {value} = e.target;
this.setState(state=>{
let tempObj = [...this.state.arrayOfAddresses]
tempObj[idx].new_address = value
return {
arrayOfAddresses: tempObj,
}
})
}
render() {
return (
<div>
{this.state.arrayOfAddresses.map((item,idx)=>
<div>
<input
name="contact"
value={item.new_address}
onChange={(e) => this.onChooseAddress(e, idx)}
/>
<button onClick={() => this.deleteInput(idx)}> x</button>
</div>
)}
<button onClick={() => this.addNewInputs()}> Add new input </button>
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
There are two things you need to change:
Set the value of <input>. The problem is that the arrayOfAddresses is set correctly, but correct values are not reflected in the input.
Add the corresponding idx value to the onChange of <input>
Here's the relevant code change:
<input name="contact" value={item.new_address} onChange={(e) => this.onChooseAddress(e, idx)}
/>
Here's the fiddle:
JSFiddle
I am using addItem to add a value to a list from another component
I am adding it to this.state.movies. It appears, however it has the inactive/noresults className applied to it.
How do I determine which styling is applied to an item that has not appeared yet (ie using addItem)? Thanks
Full example on Codesandbox is here. Add an movie to the list and you will see it gets the stying applied: https://codesandbox.io/s/3OGK2pP9
Parent component where I add the item
<CreateNew addItem={item => this.setState({ movies: [{ name: item.value.name, genres: item.genres }].concat( movies, ), })} />
Child component that creates the item
class CreateNew extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
genres: '',
};
}
handleSubmit1 = (e, value) => {
e.preventDefault();
this.props.addItem(this.state);
};
onChange = e => {
this.setState({
value: { name: e.target.value },
genres: [{ name: 'Test', type: 1 }, { name: 'Foo', type: 10 }],
});
};
render() {
const { value, genres } = this.props;
return (
<form onSubmit={this.handleSubmit1}>
Add a new movie
<input onChange={this.onChange} value={value} type="text" />
<button type="submit">Add</button>
</form>
);
}
}
Was related to filtering on my const x instead of my state this.state.movies.
I changed it from const filteredResults = andFilter({x}, Object.keys(selectedFilters)); to `const filteredResults = andFilter({this.state.movies}, Object.keys(selectedFilters));