Placeholder disappear after user interact with inputs - reactjs

I'm facing this issue in my react app:
The application is used for manage storage of a shop, there is one page in which user can add a new obj ( with name , quantity etc.) and there is a page used for UPDATE the obj.
In the "update" page I display the same form of the "new" page but with the current value as placeholder, so that the user can see what value is actually set and change just one or few of the values and leave the others as they are.
The problem is that when the user interact with the input ( like start typing then cancel ) the placeholder disappear and the value is updated as empty.
Now I would like that if user leaves the field empty after interacting with it, the placeholder should appear again, and I would like to make that if the input is left empty then it would not be updated or should be updated with the previous value.
I'm using antd library, so all inputs and stuff are taken from there.
const [data, setData]=useState({
numero: 0,
date: '' ,
nome: "",
...
//get current data
useEffect(() =>get(child(dbRef,'test/' + searchParams.get('id'))).then((snapshot)=>{
if(snapshot.exists()){
setData(snapshot.val())
console.log('[CONVERTED-DATA]',snapshot.val(), '[stato]', data);
}else{
console.log('[GET] NO data available')
}
}).catch((error)=>{
console.log(error)
})
, [])
//now "data" is populated with the values from db
function onChange(e) {
const value = e.target.value ;
setData({
...data,
[e.target.name]: value
});
console.log("[CHANGE-VALUE]", value);
console.log("[event]", e);
}
<Form>
<Form.Item
name="nome"
label="Nome"
rules={[ { type: "string", min: 3 }]}
initialValue={data.nome}
>
<Input
placeholder={data.nome}
value={data.nome}
name="nome"
onChange={onChange}
className="update-input"
/>
</Form.Item>
... more inputs

Related

In React, how to add key value to one object in array of objects

I'm new to React so please bear with me. currently working on a project and currently stuck at the last part. Per project requirement. I'm to fetch data array from an api. display each object's data on the page. and give every object an input element to be able to add a tag, display tag with the data. then be able to search based on the tag.
a simple version of the data is something like:
[{
id: "1",
name: "aaa",
info: "xxx"
},{
id: "2",
name: "bbb",
info: "yyy"
},{
id: "3",
name: "ccc",
info: "zzz"
}]
i first created a newdata array when i fetch data, and added empty string for tags key.
useEffect(()=>{
axios.get(apiurl)
.then(response=>{
const students = response.data.students;
const newdata = students.map((o)=>({
...o,
tags: []
}))
setNewdata(newdata)
})
},[apiurl])
so now newdata is like:
{
id: "4",
name: "ddd",
info: "xyz",
tags: []
}
I'm rendering the data array using the .map() and displaying each Object(student) and displaying all the data which includes the input area to add the tags. below is the part of code that displays the tags and input box that allows users to enter tags:
newdata.map(function(student, index){
return(
...//other code before this
<div className="tagbox">
<div className='tags'>
{student.tags.map(function(tag, index){
return <span key={index}>{tag}</span>
})}
</div>
<input
id={student.id}
type="text"
placeholder='tag'
value={newtag}
onChange={e=> setNewtag(e.target.value)}
onKeyDown={handleKeyDown} />
</div>
)}
so basically i'm running a handlekeydown function for the enter key. so when the user finishes typing and hits enter. a new tag should display above and have it's value added to the newdata array so i can later match for the search function.
problem is I'm unable to figure out the correct code and syntax to execute this logic. So Far all i can come up with is this:
function handleKeyDown(e){
if (e.key === "Enter"){
for (let i = 0; i < newdata.length; i++){
if (newdata[i].id == "1"){
newdata[i].tags.push(newtag);
}
}
setNewtag(''); //this is to empty the input field
}
}
as you can see, I'm trying to loop through every object (student) in the data. and when the student id matches. we add the data. else we skip. I've hard coded the value to make it work only on the first student. I've tried many methods but i either add to all or add to none.
now I've also been able to add an id to each of the input fields in the jsx like so:
<input id={student.id}/>
now, every input field has a unique id identical to the student's id from the data that's displayed above.
So the question is how should I change my handlekeydown function so that every time i enter a tag it only adds to the data with the same id.
PS: every object (student) that's displayed has its own input element from my .map() rendering.
//my goal is to type "!##" into the input#4, hit enter and have
{
id: "4",
name: "ddd",
info: "xyz",
tags: ["!##"]
}
you are only rendering 1 input.
Use the same approach to render 1 input for each student like you did for the span
<div className='tags'>
{student.tags.map(function(tag, index){
return <span key={index}>{tag}</span>
})}
</div>
<div>
{student.map(function(stud, idx){
return <input
id={stud.id}
key={idx}
type="text"
placeholder='tag'
value={newtag}
onChange={e=> setNewtag(e.target.value)}
onKeyDown={(e) => handleKeyDown(e, stud.id)} />
})}
<div>
Also remember to update your handleKeyDown function by adding the student id to the signature and use it properly when needed
function handleKeyDown(e, studentId){
if (e.key === "Enter"){
for (let i = 0; i < newdata.length; i++){
if (newdata[i].id == studentId){
newdata[i].tags.push(newtag);
}
}
setNewtag(''); //this is to empty the input field
}
}
You'll find out there's another issue, in fact all your input are using the same newTag variable. I'll leave that to you :)

How to save draft of a long form in ReactJS

I have a long form with over 80 input fields. After submission, it works fine and saves data in a neo4j database. I need to have 'Save Draft' function in case the users want to fill the form later. How can I implement it ?
Edit:
I am trying to save the form data (including empty input fields) into the neo4j database.
I have an array and the form in the constructor:
constructor(props){
super(props);
this.state = {
mydata: {
data1: {},
data2: {},
}
};
this.saveAsDraft = this.saveAsDraft.bind(this);
<form>
<MyInput
title="Name"
inputs={[{title: "name", inputType: "text" }]}
onChange={this.handleChange}
type="freeText"
/>
<MyInput
title="Email"
inputs={[{title: "email", inputType: "text" }]}
onChange={this.handleChange}
type="freeText"
/>
</form>
}
And I used following method to handle the form submission:
async saveAsDraft(){
const name = this.state.mydata['data1']['name'];
const email = this.state.mydata['data1']['email'];
const neo4j = require('neo4j-driver')
const driver = neo4j.driver('bolt://localhost:7687', neo4j.auth.basic('neo4j', 'pass'))
const session = driver.session({database: 'neo4j'})
const txc = session.beginTransaction()
try {
const result = await txc.run(
'CREATE (:Person { name: $name, email: $email })',
{
name: name,
email: email
}
)
await txc.commit()
console.log('commited')
} catch (error) {
console.log(error)
await txc.rollback()
console.log('rolled back')
} finally {
await session.close()
}
}
It works when the input fields are filled. But it shows following error when input fields are empty:
Neo4jError: Expected parameter(s): name, email
how can i handle the empty input fields here ? I want to execute the cypher queries and create nodes and relations with empty input values.
I also need to extract the data later to populate them to the form when the user wants to continue editing the form later.
If it's required to access the form answers from different devices or browsers, then you'll have to store the data into database.
If it's enough, that the users can access the saved form data only on same browser, you can use local storage to achieve that. Some state management libraries like redux and mobxstate tree offer persist feature, so you can persist the whole state of your app in the local storage with minor config.

Why do I have to push enter twice in this react input form?

I'm new to react and have made a basic weather app. When it initialises, it automatically uses location coordinates to populate the fields. However, I also have included an input form, allowing the user to type in a different city if they choose to do so.
I store the users search term in state. If a search term has been inputted, I would like the app to make an API call based on that term, otherwise just use the coordinates provided.
//state to hold search term
const [manualSearch, setManualSearch] = useState({
searchTerm: ""
})
// function to retrieve weather data from weather api based on coordinates or search term
function getWeather() {
let currentWeatherRequest = ""
//if there is a search term
if (manualSearch.searchTerm !== "") {
currentWeatherRequest = `http://api.openweathermap.org/data/2.5/weather?q=${manualSearch.searchTerm}&appid=${API_KEY}&units=metric`
} else {
// api based on coordinates
currentWeatherRequest = `http://api.openweathermap.org/data/2.5/weather?lat=${coords.latitude}&lon=${coords.longitude}&appid=${API_KEY}&units=metric`;
}
fetch(currentWeatherRequest)
....etc
Here is the function to update state and refresh the search if a user inputs a town to search and presses enter
// search for town manually
function handleManualSearch(event) {
if (event.which === 13) {
setManualSearch({
searchTerm: event.target.value
})
getWeather()
}
}
I pass this function to a component which contains the input field via props. Here is the code for the input#
<input id="testingthis" placeholder="Enter a new location e.g. London" name="city" type="text" onKeyPress={(event) => props.manualSearch(event)} />
This all works, but only when I press enter twice. I figured it would have something to do with the fact that setState is asynchronous, so I tried adding the getWeather function as a callback like so:
// search for town manually
function handleManualSearch(event) {
if (event.which === 13) {
setManualSearch({
searchTerm: event.target.value
}, () => { getWeather()})
}
}
...but have the same issue with this. What am I missing? I'm fairly new to react so brief explanation would be helpful also.
thanks!

react address lookup issue with changing final input value

I am building an address lookup functionality for an app, and my previous 2 questions have thus far, been unable to garner an answer. I am on the last step, and need to figure this out, so I turn to you stack overflow.
I start with an input, a button and a target input:
<input type='text' name='postcode-lookup' onChange={this.postcodeChange} />
<button onClick={this.searchPostcode}>Search</button>
<input type='text' name='target-for-data' />
Easy enough. Now for the functions attached to both of those elements:
postcodeChange = {e} => {
this.setState({'postcode': e.target.value});
}
searchPostcode = () => {
this.setState({'visible': true});
if(this.state.postcode.length . 0){
Axios.get('postcode look up api here')
.then(response => {
this.setState({'response': response.data});
})
}
}
Ok here, we have 3 state items: postcode, which we will set to an empty string '', visible, which is initially set to true, and response, which is an empty array, that we then populate with the response data of address objects.
My next step, was to display those addresses, so inside the render, I set a const that maps over the response array like so:
const result = this.state.response.map((item) => {
if(this.state.visible === true){
return(
<p key={item.id} onClick={addressClick}>{item.address1Field}</p>
)
}else {
}
})
Ok, so when we click the button, it will return a p tag filled with address data from the array. This also has a function, which is where my problem lies.
Inside of this function I set the visible state item to false, so that the addresses disappear. Easy enough.
But how do I then take the address that I clicked on, and populate it into the original input we started with?
I have tried many things, from setting the state in addressClick, targeting the innerHTML, e.target.value, e.target.innerHTML, and so on and so on for hours.
Any takers? Ideas?

I need to add predefined - '#domain' when user type on the form?

My form only allows user to type their email without #domainname. for e.g. tomdickharyy#email.com the bit from #email.com is predefined. My handle change function will concatenate "#email.com" at the end. Once the form is submitted on my state i set {email : ''} and the form is empty as expected.
The problem is when I type a new email the form will concatenate #email.com for every letter typed. I don't know how to fix this problem, any help would be great.
handleChange({ target: {name, value} }){
const domainEmail = '#email.com';
name === "email" ? this.setState({ [name]: value + domainEmail}) : this.setState({ [name]: value});
} ```
You have two options:
1) Remove domainEmail and append it again on each change. Some very rude code:
handleChange({ target: {name, value} }) {
const domainEmail = '#email.com';
name === 'email'
? this.setState({ [name]: value.replace(domainEmail, '') + domainEmail })
: this.setState({ [name]: value });
}
2) Move out domainEmail to be purely representational, next to the input field. This is how many UI frameworks do it, see for example Bootstrap: https://getbootstrap.com/docs/4.3/components/input-group/
Then when you submit, remember to append the domain. I recommend this approach, it makes it much less fragile, removing many edge cases. It isalso very clear for the user that they should not type their email.

Resources