Dynamic variables in react js - javascript

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.

Related

react onchange event overrides text field value

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);

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"
/>
)}

Get result as Array in Antd Form

I want my form to submit price data as an array format, currently, my form has a map, look like
{data.Type &&
<div>
{data.Type.map((datamapped)=>
<div key={datamapped._id}>
<p>{datamapped.TypeName}</p>
<Form.Item>
{getFieldDecorator(`price.${datamapped._id}.basePrice`)(
<Input placeholder="Base Price"/>,
)}
{getFieldDecorator(`price.${datamapped._id}.higherPrice`)(
<Input placeholder="Higher Price"/>,
)}
</div>
)}
</div>
}
Here I mapping my Type here and included, basePrice and higherPrice fields
result is :
price:
{
'5dc2913cf9e2372b11db4252': { basePrice: '0', higherPrice: '0' },
'5dc2a109f9e2372b11db4253': { basePrice: '0', higherPrice: '0' }
},
I want the above result as an array format how to do it?
Another way to get values as an array is to provide an array as FormItem name attribute like this:
<FormItem name={['wrapperName', index, 'propertyName']}>
<Input/>,
</FormItem>
Try change datamapped._id to [index]
{data.Type &&
<div>
{data.Type.map((datamapped, index)=>
<div key={datamapped._id}>
<p>{datamapped.TypeName}</p>
<Form.Item>
{getFieldDecorator(`price[${index}].basePrice`)(
<Input placeholder="Base Price"/>,
)}
{getFieldDecorator(`price[${index}].higherPrice`)(
<Input placeholder="Higher Price"/>,
)}
</div>
)}
</div>
}
You can try this:
var json = {
price: {
"5dc2913cf9e2372b11db4252": { basePrice: "0", higherPrice: "0" },
"5dc2a109f9e2372b11db4253": { basePrice: "0", higherPrice: "0" }
}
};
json.price = Object.entries(json.price);
console.log(json);
πŸ“’my Dynamic Form's note
antd#3.x
const MyForm = ({ form = {} }) => {
const { getFieldValue, getFieldDecorator } = form;
const [defaultData, setDefaultData] = useState({});
const add = ()=>{
// 🚩
defaultData.price.push({basePrice: 'zz',
higherPrice: 'hzz',key: new Date().getTime()})
}
return (
<Form>
<Form.Item>
{Array.isArray(defaultData.price) && // 🚩
defaultData.price.map((datamapped, index) => {
return (
<div key={datamapped.id||datamapped.key}>
{getFieldDecorator(`price[${index}].basePrice`, { // 🚩
initialValue: datamapped.basePrice,
})(<Input placeholder="Base Price" />)}
{getFieldDecorator(`price[${index}].higherPrice`, {
initialValue: datamapped.higherPrice,
})(<Input placeholder="Higher Price" />)}
</div>
);
})}
</Form.Item>
<Button onClick={add}> + </Button>
</Form>
);
};
export default Form.create()(MyForm);
data structure
|-- price array[object]
|-- basePrice string
|-- higherPrice string
🚩must use useState to assist setFieldsValue
const [defaultData, setDefaultData] = useState({})
setDefaultData({
price: [
{
basePrice: 'xx',
higherPrice: 'hxx',
},
{
basePrice: 'yy',
higherPrice: 'hyy',
},
],
});
⚠️ setFieldsValue only works in the first calling

Use variable in .map function react

I am a new reacter. I have a problem when i use map and variables could you help me?
i don't know how to input the variables in options like value={name.value}
const info = [
{ key: "λ‹‰λ„€μž„", value: "name" },
{ key: "지역", value: "area" },
{ key: "생일", value: "birthday" },
{ key: "ν‚€", value: "tall" },
{ key: "λͺΈλ§€", value: "body" },
{ key: "직업", value: "job" },
{ key: "νšŒμ‚¬", value: "company" },
{ key: "학ꡐ", value: "school" },
{ key: "ν•™λ ₯", value: "background" },
{ key: "쒅ꡐ", value: "religion" },
{ key: "흑연", value: "smoking" },
{ key: "카카였 아이디", value: "kakaoid" }
];
<Grid container spacing={3}>
{info.map((info, index) => (
<Grid item xs={12} sm={6} key={index}>
<TextField
required
id={index}
name={info.value}
label={info.key}
value={}
onChange={}
fullWidth
/>
</Grid>
))}
</Grid>
I want to make it like that
<Grid item xs={12} sm={6} key={1}>
<TextField
required
id={1}
name={"name"}
label={"λ‹‰λ„€μž„"}
value={name.value}
onChange={name.onChange}
fullWidth
/>
</Grid>
If you want to set the value for the each text field .
Define a state variable for each of the text-field (use name of the each text fields)and bind an on-change event.
In on-change bind an this and get the name & value , based on on-change set the value to the name state for each text field
Now pass the state to each of the text-field .
You should aware of state an props in react , on-change events.
For example
constructor(props){
super(props);
this.state = {
value:''
}
}
inputchange = (event) =>{
this.setState({
value:event.target.value
})
}
render(){
return (
<div className="todolist">
<input type="text" value={this.state.value} onChange={this.inputchange}/>
<input type="button" value="Submit" onClick={this.props.handleclick.bind(this,this.state.value)}/>
</div>
);
}
}

Change value in setState when your data is in array

I have contactNumber with the data of array, and Im using native-base to view the data.
this.state = {
leadProfile: {
contactNumber: [
{
"lead_contact_number": "0912 312 412312",
"lead_contact_number_type": {
"lead_contact_number_type": "Mobile"
}
},
{
"lead_contact_number": "1231234rqdasd",
"lead_contact_number_type": {
"lead_contact_number_type": "Mobile"
}
},
{
"lead_contact_number": "0325 658 58996",
"lead_contact_number_type": {
"lead_contact_number_type": "Mobile"
}
}
]
},
contactNumber1: '',
contactNumber2: '',
contactNumber3: '',
contactNumber4: '',
contactNumber5: '',
};
}
the contactNumber1,2,3,4,5, this are all the containers when the data is change I want also get the data on the specific field
This is my function and also renderedData ....
arrayOfContacts is my array of data, sorry for the code I know not good work around but this what I think to be coded, feel free if there's good workaround..
The goal in here is to display and change the value of lead_contact_number
renderContactForm = () => {
let arrayOfContacts = _.map(this.state.leadProfile.contactNumber)
if (_.isEqual(this.state.leadProfile.contactNumber.length, 0) || _.isEqual(this.state.leadProfile.contactNumber.length, 1)) {
return (
...
)
} else if (_.isEqual(this.state.leadProfile.contactNumber.length, 2)) {
return (
....
)
} else if (_.isEqual(this.state.leadProfile.contactNumber.length, 3)) {
return (
<View>
<Form>
<Item floatingLabel style={{ paddingLeft: 4 }}>
<Label style={{ fontSize: 15, color: '#a0a0a0', paddingLeft: 4 }}>
{arrayOfContacts[0].lead_contact_number_type.lead_contact_number_type}
</Label>
<Input
autoCapitalize="number"
underlineColorAndroid='transparent'
onChangeText={(text) =>
this.setState({
...this.state,
leadProfile: {
...this.state.leadProfile.contactNumber[0],
lead_contact_number: text
},
contactNumber1: text
})}
value={this.state.leadProfile.contactNumber[0].lead_contact_number} />
</Item>
</Form>
<Form>
<Item floatingLabel style={{ paddingLeft: 4 }}>
<Label style={{ fontSize: 15, color: '#a0a0a0', paddingLeft: 4 }}>
{arrayOfContacts[1].lead_contact_number_type.lead_contact_number_type}
</Label>
<Input
autoCapitalize="number"
underlineColorAndroid='transparent'
onChangeText={(text) => this.setState({
...this.state,
leadProfile: {
...this.state.leadProfile.contactNumber[1],
lead_contact_number: text
},
contactNumber2: text
})}
value={this.state.leadProfile.contactNumber[1].lead_contact_number} />
</Item>
</Form>
<Form>
<Item floatingLabel style={{ paddingLeft: 4 }}>
<Label style={{ fontSize: 15, color: '#a0a0a0', paddingLeft: 4 }}>
{arrayOfContacts[2].lead_contact_number_type.lead_contact_number_type}
</Label>
<Input
autoCapitalize="number"
underlineColorAndroid='transparent'
onChangeText={(text) => this.setState({
...this.state,
leadProfile: {
...this.state.leadProfile.contactNumber[2],
lead_contact_number: text
},
contactNumber3: text
})}
value={this.state.leadProfile.contactNumber[2].lead_contact_number} />
</Item>
</Form>
</View>
)
} else if (_.isEqual(this.state.leadProfile.contactNumber.length, 4)) {
return (
...
)
}
}
The data will be true on _.isEqual(this.state.leadProfile.contactNumber.length, 3)
When I'm trying edit the text field the data is change and back to the default number.
That is a lot of markup to condense. Let me show you a simpler way to render your form and update the corresponding values. See this sandbox for action: https://codesandbox.io/s/boring-hill-i3prc
class NumberForm extends Component {
constructor(props) {
super(props);
this.state = {
leadProfile: {
contactNumber: [
{
lead_contact_number: "0912 312 412312",
lead_contact_number_type: {
lead_contact_number_type: "Mobile"
}
},
{
lead_contact_number: "1231234rqdasd",
lead_contact_number_type: {
lead_contact_number_type: "Mobile"
}
},
{
lead_contact_number: "0325 658 58996",
lead_contact_number_type: {
lead_contact_number_type: "Mobile"
}
}
]
}
};
}
handleChange = (e, numberIndex) => {
const { contactNumber } = this.state.leadProfile;
const updatedNumbers = contactNumber.map((number, index) => {
if (index === numberIndex) {
return {
...number,
lead_contact_number: e.target.value
};
} else {
return {
...number
};
}
});
this.setState({
leadProfile: {
...this.state.leadProfile,
contactNumber: updatedNumbers
}
});
};
createForm = () => {
const { contactNumber } = this.state.leadProfile;
return contactNumber.map((number, numberIndex) => {
return (
<div>
<input
value={number.lead_contact_number}
onChange={e => this.handleChange(e, numberIndex)}
/>
<label>
{number.lead_contact_number_type.lead_contact_number_type}
</label>
</div>
);
});
};
render() {
return <div>{this.createForm()}</div>;
}
}
Highlevel Points:
We will use the contact-array to create the inputs like you did
initially. But we will use the index value provided by .map() as
well.
We define an event-handler function to update our array-state
whenever the user enters an input, it accepts an index as an
argument.
During the setup of our inputs, we give it an event-listener, pairing
it with handleChange() function, and pass in the index of the
corresponding object we are iterating over in the .map()
This structure lets us effectively update the number for each input
even when the state value is an array of objects.
Keep your structure like this, and you should be able to freely swap out the mark-up like input and label with your choice of components from whatever library you're using.

Categories