react onchange event overrides text field value - javascript

I wanna asign the email value to the text field but it does not work but When i tried to put it on a text the value is there like for example on a span , the value of emailAddress should be the value of the textfield , any idea why does this not work guys ?
Thanks.
<span style={{paddingTop:5}}>
Full Name: {email.firstName} {email.lastName} {email.emailAddress}
</span>
#html
<TextField
type="text"
style={{ width: "95%" }}
onChange={($event) => emailOnChange($event, prop.id, mIndex)}
label="Email Address"
variant="filled"
name={email.emailAddress}
value={email.emailAddress}
defaultValue={email.emailAddress}
// InputProps={{
// endAdornment: fetching ? (
// <CircularProgress />
// ) : null,
// }}
/>
#ts snippet
const emailOnChange = debounce(function (event, id, index) {
setRoleId(id);
setEmailCurrentIndex(index);
const payload: IYardUserRequestPayload | InitialReqPaylod = {
accountId: 1,
searchString: event.target.value,
};
setFetching(true);
dispatch(getYardUser(payload));
}, 500);

Related

MUI Autocomplete not displaying name after selecting option for first time

I'm seeing a weird issue with Material UI's Autcomplete component. I have the following code at present:
const [isContactListInitialised, setContactListInitialised] = useState(false);
const toggleContactListInitialised = () => { setContactListInitialised(state => !state) }
const [contactList, setContactList] = useState([]);
...
const newJobStructure = {
name: "",
isJobStartLinked: true,
isJobFinishLinked: true,
outgoingDateTime: moment(),
jobStartDateTime: moment(),
returningDateTime: moment().add(1, "hours"),
jobFinishDateTime: moment().add(1, "hours"),
contactId: -1,
jobTypeId: -1,
deliveryLocationId: -1,
}
const [newJob, setNewJob] = useState(newJobStructure)
const handleNewJobChange = (event) => setNewJob({...newJob, [event.target.name]: event.target.value})
...
useEffect(() => {
if(!isContactListInitialised) { fetchAllContacts(); }
}, [])
...
return(
...
<Autocomplete
id="contacts-group"
options={contactList}
getOptionLabel={(contact) => contact.contactName || ''}
groupBy={(contact) => contact.firstLetter}
value={newJob.contactId}
onChange={(event, value) => setNewJob({...newJob, contactId: value.contactId})}
freeSolo
sx={{ width: "100%", minWidth: 400 }}
renderInput={(params => <TextField {...params} label="Contact Name" />)} />
...
)
Now, as the title explains, when the form is displayed initially, I see the TextField as expected, along with it being blank and having "Contact Name" in it. When I click it, it displays an ordered list, again, as expected.
When I then select an option, the contact.contactName value displays for a split second and then it disappears. However, it still looks populated as the label retreats to the top left corner of the box.
If I then tap the same option again, the contact.contactName value then displays as expected.
What on Earth could be going on here? Is it a bug with my code, or MUI's? how would I be able to get around this?
Thanks!
It seems your option list is in format { contactId: number, contactName: string } which is an object, and the autocomplete has been controlled with value property which is mapped with number type. To solve this issue, do the below changes in your code.
Add property contactName in newJobStructure
const newJobStructure = {
name: "",
isJobStartLinked: true,
isJobFinishLinked: true,
outgoingDateTime: moment(),
jobStartDateTime: moment(),
returningDateTime: moment().add(1, "hours"),
jobFinishDateTime: moment().add(1, "hours"),
contactId: -1,
contactName: '', // Add contactName Property
jobTypeId: -1,
deliveryLocationId: -1
};
Update controlled value and onChange event of autocomplete control as below
<Autocomplete
id="contacts-group"
options={contactList}
getOptionLabel={(contact) => contact.contactName || ''}
groupBy={(contact) => contact.firstLetter}
// value={newJob.contactId}
value={{ contactId: newJob.contactId, contactName: newJob.contactName }} // Update controlled value
// onChange={(event, value) => setNewJob({ ...newJob, contactId: value.contactId })}
onChange={(event, value) => setNewJob({ ...newJob, contactId: value?.contactId ?? -1, contactName: value?.contactName ?? '' })} // Update onChange event
isOptionEqualToValue={(option, value) => option.contactId === value.contactId }
freeSolo
sx={{ width: "100%", minWidth: 400 }}
renderInput={(params => <TextField {...params} label="Contact Name" />)}
/>

How to avoid checking if a function exists before calling it?

I need access to the formik props outside of the form.
While using refs, how can I avoid checking if a function exists before calling it?
function BasicInfo({ event, initialValues, onSubmit }: Props) {
const { coords } = useLocation();
const { data: places, forward, getStaticMapUrl } = useMapbox();
const { data: games, search, leanGame } = useGames();
const throttle = useThrottle();
const formRef = useRef<FormikProps<BasicInfoValues>>(null);
const initValues: BasicInfoValues = initialValues || {
title: '',
game: { name: '' },
address: '',
starts_at: '',
ends_at: '',
coords: '',
};
const { setFieldValue, values, handleChange } = formRef.current || {};
const handleEditLocationClick = function () {
if (!setFieldValue) return;
setFieldValue('address', '');
setFieldValue('coords', '');
};
const renderLocation = function () {
if (!values) return null;
const { coords: coordinates, address } = values;
if (coordinates && address) {
const staticMapUrl = getStaticMapUrl({
coords: JSON.parse(coordinates),
width: 600,
height: 165,
});
const { street, city } = parseAddress(address);
return (
<div className="basic-info__location">
<div className="basic-info__location__image-container">
<Image
src={staticMapUrl}
alt="static map"
layout="fill"
objectFit="fill"
/>
</div>
<div className="basic-info__location__address-container">
<div className="basic-info__location__address-container__address">
<span className="basic-info__location__street">{street}</span>
<span className="basic-info__location__city">{city}</span>
</div>
<Button
text="Edit location"
color="secondary"
size="small"
onClick={handleEditLocationClick}
/>
</div>
</div>
);
} else {
return (
<AutoComplete<Feature>
name="address"
value={values.address}
onChange={(e) => {
if (!handleChange) return;
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
if (!coords) return;
forward({
coords,
q: e.target.value,
});
}, 500);
}}
label="Venue location"
placeholder="Search for a venue or address"
items={places}
Input={InputGroup}
itemRenderer={(item) => <AddressItem placeName={item.place_name} />}
onItemClick={(item) => {
if (!setFieldValue) return;
setFieldValue('address', item.place_name);
setFieldValue('coords', JSON.stringify(item.center));
}}
/>
);
}
};
return (
<Formik initialValues={initValues} onSubmit={onSubmit} innerRef={formRef}>
{({ values, handleChange, setFieldValue }) => (
<Form id="basic-info" className="basic-info">
<FormSection
title="Basic Info"
description="Name your event and tell gamers what game will be played. Add
details that highlight what makes it unique."
icon="segment"
>
<InputGroup
name="title"
value={values.title}
onChange={handleChange}
label="Event title"
placeholder="Be clear and descriptive"
/>
<AutoComplete<Game>
name="game.name"
value={values.game.name}
onChange={(e) => {
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
search({ name: e.target.value, limit: 5 });
}, 500);
}}
label="Featured game"
placeholder="Search games"
Input={InputGroup}
items={games}
itemRenderer={(item) => <GameItem game={item} />}
onItemClick={(item) => {
const game = leanGame(item);
setFieldValue('game', game);
}}
/>
<span>
Need game ideas?{' '}
<Link href="/" passHref>
<a className="link">Browse games by category</a>
</Link>
</span>
</FormSection>
<FormSection
title="Location"
description="Help gamers in the area discover your event and let attendees know where to show up."
icon="map"
>
{renderLocation()}
</FormSection>
<FormSection
title="Date and time"
description="Tell gamers when your event starts and ends so they can make plans to attend."
icon="date_range"
>
<InputGroup
name="starts_at"
value={values.starts_at}
onChange={handleChange}
label="Event starts"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
<InputGroup
name="ends_at"
value={values.ends_at}
onChange={handleChange}
label="Event ends"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
</FormSection>
</Form>
)}
</Formik>
);
}
Specifically this function where I check if setFieldValue exists:
const handleEditLocationClick = function () {
if (!setFieldValue) return;
setFieldValue('address', '');
setFieldValue('coords', '');
};
There are other functions that also need formik props so I will have to do these checks. I guess I could pass in setFieldValue function as an argument to the handleEditLocationClick function, but that doesn't seem like a good practice.
You should not be using ref at all here. Just pass the render prop parameter down to your helper functions:
function BasicInfo({ event, initialValues, onSubmit }: Props) {
const { coords } = useLocation();
const { data: places, forward, getStaticMapUrl } = useMapbox();
const { data: games, search, leanGame } = useGames();
const throttle = useThrottle();
const initValues: BasicInfoValues = initialValues || {
title: '',
game: { name: '' },
address: '',
starts_at: '',
ends_at: '',
coords: '',
};
function renderLocation({ values, handleChange, setFieldValue }) {
const handleEditLocationClick = function () {
setFieldValue('address', '');
setFieldValue('coords', '');
};
const { coords: coordinates, address } = values;
if (coordinates && address) {
const staticMapUrl = getStaticMapUrl({
coords: JSON.parse(coordinates),
width: 600,
height: 165,
});
const { street, city } = parseAddress(address);
return (
<div className="basic-info__location">
<div className="basic-info__location__image-container">
<Image
src={staticMapUrl}
alt="static map"
layout="fill"
objectFit="fill"
/>
</div>
<div className="basic-info__location__address-container">
<div className="basic-info__location__address-container__address">
<span className="basic-info__location__street">{street}</span>
<span className="basic-info__location__city">{city}</span>
</div>
<Button
text="Edit location"
color="secondary"
size="small"
onClick={handleEditLocationClick}
/>
</div>
</div>
);
} else {
return (
<AutoComplete<Feature>
name="address"
value={values.address}
onChange={(e) => {
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
if (!coords) return;
forward({
coords,
q: e.target.value,
});
}, 500);
}}
label="Venue location"
placeholder="Search for a venue or address"
items={places}
Input={InputGroup}
itemRenderer={(item) => <AddressItem placeName={item.place_name} />}
onItemClick={(item) => {
setFieldValue('address', item.place_name);
setFieldValue('coords', JSON.stringify(item.center));
}}
/>
);
}
}
return (
<Formik initialValues={initValues} onSubmit={onSubmit} innerRef={formRef}>
{({ values, handleChange, setFieldValue }) => (
<Form id="basic-info" className="basic-info">
<FormSection
title="Basic Info"
description="Name your event and tell gamers what game will be played. Add
details that highlight what makes it unique."
icon="segment"
>
<InputGroup
name="title"
value={values.title}
onChange={handleChange}
label="Event title"
placeholder="Be clear and descriptive"
/>
<AutoComplete<Game>
name="game.name"
value={values.game.name}
onChange={(e) => {
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
search({ name: e.target.value, limit: 5 });
}, 500);
}}
label="Featured game"
placeholder="Search games"
Input={InputGroup}
items={games}
itemRenderer={(item) => <GameItem game={item} />}
onItemClick={(item) => {
const game = leanGame(item);
setFieldValue('game', game);
}}
/>
<span>
Need game ideas?{' '}
<Link href="/" passHref>
<a className="link">Browse games by category</a>
</Link>
</span>
</FormSection>
<FormSection
title="Location"
description="Help gamers in the area discover your event and let attendees know where to show up."
icon="map"
>
{renderLocation({ values, handleChange, setFieldValue })}
</FormSection>
<FormSection
title="Date and time"
description="Tell gamers when your event starts and ends so they can make plans to attend."
icon="date_range"
>
<InputGroup
name="starts_at"
value={values.starts_at}
onChange={handleChange}
label="Event starts"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
<InputGroup
name="ends_at"
value={values.ends_at}
onChange={handleChange}
label="Event ends"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
</FormSection>
</Form>
)}
</Formik>
);
}

How to conditionally show element in react JSX?

I am creating a UI dynamically using JSON data which renders input or textarea elements conditionally.
Sample JSON Data
[
{
type: "input",
val: "input text",
name: "Input Field",
editable: false
},
{
type: "text",
val: "text area text",
name: "Text area field",
editable: true
},
{
type: "text",
val: "text area text",
name: "Text area field",
editable: true
}
];
I have two values for property type one is input other is text, So if type property has value "input" then I am creating an input element otherwise textarea element.
I too have extra properties. One of them is editable, if it is set to true then user can click on edit button which which show Send button later on.
Issues
When I am clicking on edit both input field getting editable and both Edit is changing to Send
I tried using index and matching with index, but that also did not work.
My code
{data.map((li, index) => (
<div className="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
{li.type === "input" && (
<div className="divCont">
{li.editable && disabled && (
<span onClick={editComp}>Edit</span>
)}
<input
type="text"
className="form-control"
disabled={disabled}
defaultValue={li.val}
/>
</div>
)}
{li.type === "text" && (
<div className="divCont">
{li.editable && disabled && (
<span onClick={(e) => editComp(index)}>Edit</span>
)}
{disabled === false && ind === index && (
<span onClick={editComp}>Send</span>
)}
<input
type="text"
className="form-control"
disabled={disabled}
defaultValue={li.val}
/>
</div>
)}
</div>
))}
Code sandbox
Edit / Update
What I am trying to do
When the object have property editable:true it shows the edit button
just above the input or textarea element.
Then when I click on edit I want to make sure that particular input or textarea element is enabled so that user can type. Edit button should be changed to Send button to send data.
You are using the same flag 'disabled' to control the state of all your components. Therefore, when you click on send you change it to false for every field and that renders them all editable. The easiest fix would be to use a different flag for each field, but that may not scale well if you need it to.
Okay, I understand that you probably new to React and maybe even programming. Learning is good. Here some updated version of your code which does what you wanted, at least the way I have understood. Note though, I have no idea what you trying to build here, but hopefully it will give you some new start:
import React, { useState } from "react";
import "./styles.css";
import "bootstrap/dist/css/bootstrap.min.css";
function EditableArea({ val, ...rest }) {
const [disabled, setDisabled] = useState(true);
const [value, setValue] = useState(rest.value);
const handleSend = () => {
// ... send it somewhere maybe?..
console.log(value);
setDisabled(true);
};
return (
<div className="divCont">
{disabled ? (
<span onClick={() => setDisabled(false)}>Edit</span>
) : (
<span onClick={handleSend}>Send</span>
)}
<textarea
type="text"
className="form-control"
disabled={disabled}
placeholder={rest.placeholder}
onChange={(e) => setValue(e.target.value)}
value={value}
></textarea>
</div>
);
}
function Editable({ ...rest }) {
const [disabled, setDisabled] = useState(true);
const [value, setValue] = useState(rest.value || "");
const handleSend = () => {
// ... send it somewhere maybe?..
console.log(value);
setDisabled(true);
};
return (
<div className="divCont">
{disabled ? (
<span onClick={() => setDisabled(false)}>Edit</span>
) : (
<span onClick={handleSend}>Send</span>
)}
<input
type="text"
className="form-control"
disabled={disabled}
placeholder={rest.placeholder}
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</div>
);
}
let data = [
{
type: "input",
val: "input text",
name: "Input Field",
editable: false
},
{
type: "text",
placeholder: "text area text",
name: "Text area field",
editable: true,
value: ""
},
{
type: "text",
placeholder: "text area text",
name: "Text area field",
editable: true,
value: ""
}
];
function redrerInput({ type, editable, ...rest }) {
switch (type) {
case "text":
return <EditableArea {...rest} />;
case "input":
return <Editable {...rest} />;
default:
return null;
}
}
export default function App() {
return (
<div className="App">
<div className="row">
{data.map((item, i) => (
<div key={i} className="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
{redrerInput(item)}
</div>
))}
</div>
</div>
);
}
Here is CodeSandbox fork
But I would strongly recommend to read documentation about React first.

how to add dynamic validation in form using reactjs

I am using ant design in my demo application. I want to add dynamic validation of mobile number in my application.
In my form there two field
select field
input field
I want to add validation in the input field when select field in mobile(mobile number should be 10 digits).in other words I want to add validation of mobile number on input field only when user select mobile from select box
https://ant.design/components/form/
here is my code
https://codesandbox.io/s/holy-voice-o4wbj
<Form.Item>
{getFieldDecorator("value", {
rules: [
{ required: true, message: "Please input search value!" },
{ pattern: /[2-9]{2}\d{8}/, message: "Please input !" }
]
})(
<Input
style={{ width: 180 }}
// prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
placeholder="searchValue"
/>
)}
</Form.Item>
can we add this validation ?
You need to set rules as per some conditions like so:
const rules = mobileValidation
? [
{ required: true, message: "Please input a number!" },
{ pattern: /^[2-9]{2}\d{8}$/, message: "Please input 10 digit number!" }
]
: null;
Since you need only 10 digit number, you need to add ^ at the start and $ at the end of the regex pattern i.e. /^[2-9]{2}\d{8}$/
jsx
import React, { useState } from "react";
import { Form, Icon, Input, Button, Select } from "antd";
const { Option } = Select;
const SearchForm = props => {
const [mobileValidation, setMobileValidation] = useState(false);
const [isOptionSelected, setIsOptionSelected] = useState(false);
const { getFieldDecorator, getFieldsError } = props.form;
const handleSubmit = e => {
e.preventDefault();
mobileValidation && props.form.validateFields({ force: true });
};
const handleChange = value => {
setIsOptionSelected(true);
setMobileValidation(value === "mobile no");
};
const rules = mobileValidation
? [
{ required: true, message: "Please input a number!" },
{ pattern: /^[2-9]{2}\d{8}$/, message: "Please input 10 digit number!" }
// { pattern: /^\d{10}$/, message: "Please input 10 digit number!" }
]
: null;
return (
<div style={{ height: 80, display: "flex", justifyContent: "flex-end" }}>
<Form layout="inline" onSubmit={handleSubmit}>
<Form.Item>
{getFieldDecorator("searchBy", {
// initialValue: this.props.transactionEditableMode ? this.props.transactionEditableModeData.from : '',
rules: [{ required: true, message: "Please select your From!" }]
})(
<Select
style={{ width: 180 }}
placeholder="Select a option"
onChange={handleChange}
>
{[
{ text: "Caf Nos", value: "cafs" },
{ text: "mobile no", value: "mobile no" }
].map(i => {
return (
<Option key={i} value={i.value}>
{i.text}
</Option>
);
})}
</Select>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator("value", {
rules
})(
<Input
style={{ width: 180 }}
// prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
placeholder="search a number"
name="input"
/>
)}
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Search
</Button>
{!isOptionSelected && <h3>Select an option</h3>}
</Form.Item>
</Form>
</div>
);
};
const WrappedSearchForm = Form.create({ name: "search_form" })(SearchForm);
export default WrappedSearchForm;
Is that what you were looking for? let me know
Side note: Read about validateFields()
that worked
<Form.Item>
{getFieldDecorator("value", {
rules : mobileValidation ? [
{ required: true, message: "Please input a number!" },
{ pattern: /^[2-9]{2}\d{8}$/, message: "Please input 10 digit number!" }] : []
})(
<Input
style={{ width: 180 }}
// prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
placeholder="search a number"
name="input"
/>
)}

Dynamic variables in react js

i have a dynamic form in react js which gives me a output like the following -
screes are here of console logs - http://imgur.com/a/w9KYN
Object
keys
:
Array[2]
0
:
1
1
:
2
length
:
2
proto
:
Array[0]
names-1
:
"bill"
names-2
:
"wil"
noItems-1
:
50
noItems-2
:
50050
tVal-1
:
500
tVal-2
:
2520
values-1
:
500
values-2
:
500
Console.log(JSON.Stringfy(values)) -
{"keys":[1,2],"names-1":"will","values-1":200,"noItems-1":2002,"tVal-1":200,"names-2":"bill","values-2":200,"noItems-2":2002,"tVal-2":200}
if i delete one or two form items from the middle - here is the output -
{"keys":[1,4],"names-1":"will","values-1":200,"noItems-1":2002,"tVal-1":200,"names-4":"dill","values-4":300,"noItems-4":300,"tVal-4":300}
I can read the keys array in such manner -
console.log('Recived values:', values.keys);
But i want to iterate the values, Can someone help me in iterating the values? to be specific how go i get 'names-1' and 'names-2'? as the string is based on the keys array?
The code snippet is here :
hadleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if(!err){
var lis = values.keys;
this.setState({
controlKey: lis
});
lis.forEach(function(value){
/* need help to iterate here*/
})
console.log('Recived values:', values.keys);
}
})
}
my render component looks something like this -
const {getFieldDecorator, getFieldValue} = this.props.form;
const formItemLayoutWithOutLabel = {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 },
},
};
getFieldDecorator('keys', {initialValue:[]});
const keys = getFieldValue('keys');
const formItems = keys.map((k, index) => {
return(
<div>
<Row>
<Col span={6}>
<FormItem
label={index === 0 ? 'Item' : ''}
required={false}
key={k}
>
{getFieldDecorator(`names-${k}`, {
validateTrigger: ['onChange'],
rules: [{
required: true,
whitespace: true,
message: 'Please input item name',
}],
})(
<Input placeholder="Item Name" style={{width: '75%'}}/>
)}
</FormItem>
</Col>
<Col span={6}>
<FormItem
label={index === 0 ? 'Value/Unit' : ''}
required={false}
key={k}
>
{getFieldDecorator(`values-${k}`, {
validateTrigger: ['onChange'],
rules: [{
required: true,
message: 'Please input item value',
}],
})(
<InputNumber placeholder="Item value per unit" style={{width: '75%'}}/>
)}
</FormItem>
</Col>
<Col span = {6}>
<FormItem
label={index === 0 ? 'Total Unit' : ''}
required={false}
key={k}
>
{getFieldDecorator(`noItems-${k}`, {
validateTrigger: ['onChange'],
rules: [{
required: true,
message: 'Please input total number of items',
}],
})(
<InputNumber placeholder="Please input total number of items" style={{width: '75%'}}/>
)}
</FormItem>
</Col>
<Col span={6}>
<FormItem
label={index === 0 ? 'Total Value' : ''}
required={false}
key={k}
>
{getFieldDecorator(`tVal-${k}`, {
validateTrigger: ['onChange'],
rules: [{
required: true,
message: 'Total Value',
}],
})(
<InputNumber placeholder="Total Value" style={{width: '75%'}}/>
)}
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
disabled={keys.length === 1}
onClick={() => this.remove(k)}
/>
</FormItem>
</Col>
</Row>
</div>
what you want to do is define the variables to be used
const myVars = ['names', 'noItems', 'tVal', 'values']
let totalVal = 0;
lis.forEach( (value) => {
myVars.forEach( myVar => {
const key = `${myVar}-${value}`
console.log(`${key}: ${values[key]}`)
if (myVar === 'tVal') {
totalVal += values[key]
}
})
})
console.log(totalVal);
FIDDLE
so basically what i'm doing here is using the myVars array to define which keys i'm interested in. From there I am looping over the keys array to know which variable to create aka name-number. then values (the original object) bracket or sub notation on that key to get the actual value.

Categories