I am building an application using React and Electron-JS.
I am using form to display some information. To this form style is applied asstyle={{ overflow: 'scroll', height: '45vh' }}. When Form items overflows, scrollbar is added to it. But the problem is when I scroll down, the items were hidden till then will not be rendered fully. But when the screen is resized, they become visible again. This happens like once in 10 times.
I cannot reproduce it always.
I think React is not rendering the items which are not visible on the visible screen to gain performance.
Following is the code for the form component:
const style = { margin: '5px', height: '3.3vh', labelAlign: 'left' };
const inputStyle = { padding: '3px', height: '26px' };
export const ObjectPane = React.memo(({ objDetails }) => (
<Form
fields={Object.keys(objDetails).map(key => {
let value = null;
if (objDetails[key] === undefined) value = 'NA';
else if (objDetails[key] === null) value = 'No Value';
else value = objDetails[key];
return {
name: [`${key}`],
value
};
})}
style={{ overflow: 'scroll', height: '45vh' }}
onScroll={() => {}}
>
<Form.Item label="Object" name="obj" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Object Id" name="objId" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Distance Long. [m] " name="longitude" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Distance Lat. [m] " name="lateral" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Velocity Long. [m/s]" name="vel_long" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Velocity Lat. [m/s]" name="vel_lat" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="2 Wheeler Confidence" name="conf_twowheel" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="4 Wheeler Confidence" name="conf_fourwheel" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item
label="Pedestrian Confidence"
name="conf_pedestrian"
style={style}
>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item
label="Stationary Confidence"
name="conf_stationary"
style={style}
>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Object Type" name="obj_type" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="Type Confidence" name="conf_obj" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="long_cu_aeb" name="long_cu_aeb" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="cross_vru_aeb" name="cross_vru_aeb" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="long_cu_fcw" name="long_cu_fcw" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="cross_vru_fcw" name="cross_vru_fcw" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
<Form.Item label="cyc_ped_tipl" name="cyc_ped_tipl" style={style}>
<Input style={inputStyle} disabled />
</Form.Item>
</Form>
));
How can I make this work as expected? Please let me know if any more information needs to be added here.
Related
I tried making two input fields one for dimensions and one for weight, and both had seperate select drop down to allow the user to select a unit. I saw on Ant design docs that they had something similar, so I tried using that.
This is how I wanted it to be like:
Now i have filled my form with many other fields and they work just fine, on clicking the save button, I am not getting any data entered in the fields for dimensions or weight, nor their units. I have a standard save function which is called on 'onFinish' event:
const handleSubmit = (data) => {
console.log('data', data);
submit(data);
};
This is my code to generate the fields.
<Form onFinish={handleSubmit} >
<Row style={{ justifyContent: 'left' }}>
{<Col span={8}>
<div className="p-2 lbh-input">
<Form.Item
name="dimensions"
key="dimensions"
label="Dimensions (l x b x h)">
<Input.Group>
<Input
key='length'
name='length'
style={{ width: '15%' }}
type="number"
/>
<Input
key='breadth'
name='breadth'
style={{ width: '24%' }}
addonBefore="x"
type="number"
/>
<Input
key='height'
name='height'
style={{ width: '25%' }}
addonBefore="x"
type="number"
/>
<Select name='dimension_unit' key='dimension_unit' defaultValue="cm">
<Option value="mm">mm</Option>
<Option value="cm">cm</Option>
<Option value="inch">inch</Option>
<Option value="feet">feet</Option>
<Option value="m">m</Option>
</Select>
</Input.Group>
</Form.Item>
</div>
</Col>
}
{
<div className="p-2">
<Form.Item
key="weight"
name="weight"
label="Weight">
<Input.Group>
<Input
style={{ width: '50%' }}
type="number"
key="weight"
name="weight"
label="Weight"
className='noborderradius'
/>
<Select defaultValue="kg" name="weight_unit" key="weight_unit">
<Option value="kg">kg</Option>
<Option value="tonne">tonne</Option>
<Option value="g">g</Option>
</Select>
</Input.Group>
</Form.Item>
</div>}
</Row>
<button>SUBMIT</button>
</Form>
As you can see, i have tried using everythihg I can like label,name,key but no matter what happens, I get no data being sent no matter what I type in these two fields. What am i missing? Am i doing something wrong with <Form.item> ?
My ant design version is
"antd": "^4.3.4",
Input.Group is just for layout purposes, it does not group all your inputs into a single Form.Item. You still need to wrap all your inputs with Form.Item and attach the names there. Use the noStyle property to not override the Input.Group styling.
Also, defaultValue will give you this warning:
Warning: [antd: Form.Item] defaultValue will not work on controlled Field. You should use initialValues of Form instead.
So you can just do as it says, I've removed the styling for brevity
<Form
onFinish={handleSubmit}
initialValues={{ dimension_unit: 'cm', weight_unit: 'kg' }}
>
<Form.Item label="Dimensions (l x b x h)">
<Input.Group>
<Form.Item name="length" noStyle>
<Input type="number" />
</Form.Item>
<Form.Item name="breadth" noStyle>
<Input type="number" addonBefore="x" />
</Form.Item>
<Form.Item name="height" noStyle>
<Input type="number" addonBefore="x" />
</Form.Item>
<Form.Item name="dimension_unit" noStyle>
<Select>
<Option value="mm">mm</Option>
<Option value="cm">cm</Option>
<Option value="inch">inch</Option>
<Option value="feet">feet</Option>
<Option value="m">m</Option>
</Select>
</Form.Item>
</Input.Group>
</Form.Item>
<Form.Item label="Weight">
<Input.Group>
<Form.Item name="weight" noStyle>
<Input type="number" />
</Form.Item>
<Form.Item name="weight_unit" noStyle>
<Select>
<Option value="kg">kg</Option>
<Option value="tonne">tonne</Option>
<Option value="g">g</Option>
</Select>
</Form.Item>
</Input.Group>
</Form.Item>
<button>SUBMIT</button>
</Form>
Note that you don't need the key property.
demo: https://stackblitz.com/edit/react-41wakw?file=demo.tsx
What you can do is instead of wrapping the entire <Input.Group> with a single <Form.Item> you need to wrap each <Input> with its own <Form.Item> and give each one a unique key and name.
Working Code Sandbox
<Input.Group>
<Form.Item
name="length"
key="length"
>
<Input
style={{ width: '15%' }}
type="number"
/>
</Form.Item>
<Form.Item
key='breadth'
name='breadth'
>
<Input
style={{ width: '24%' }}
addonBefore="x"
type="number"
/>
</Form.Item>
...
</Input.Group>
const updateCreateFormField = (e) => {
const { name, value } = e.target;
setCreatForm({
...createForm,
[name]: value,
})
console.log({ name, value });
};
//The onChange variable in the fields is updated on the above code. I am unable to find the solution towards making the below fields function properly. I tried using formik's setFieldValue however It didn't work
const formik = useFormik({
initialValues: {
Name: "",
Address: "",
phoneNumber: "",
Email: "",
}
})
The below code is the return function:
return (<div className="App">
{updateForm._id && (<div>
<h2>Update Customer:</h2>
<Box height={20} />
<Formik initialValues={formik.initialValues}
validationSchema={object({
Name: string().required("Please enter a name").min(3, "Name is too short"),
Address: string().required("Please enter an address").min(3, "Address is too short"),
phoneNumber: number().required("Please enter a phone number").min(4, "Phone number is too short"),
Email: string().required("Please enter an email").email("Invalid email"),
})}
onSubmit={(values, formikHelpers) => {
console.log(values);
formikHelpers.resetForm();
}}
>
{({ errors, isValid, touched, dirty }) => (
<Form onSubmit={updateCustomer}>
<Field name="Name" type="name" as={TextField} variant="outlined" color="primary" label="Name" fullWidth
onChange={handleUpdateFieldChange}
value={updateForm.Name}
error={Boolean(errors.Name) && Boolean(touched.Name)}
helperText={Boolean(touched.Name) && errors.Name}
/>
<Box height={14} />
<Field name="Address" type="Address" as={TextField} variant="outlined" color="primary" label="Address" fullWidth
onChange={handleUpdateFieldChange}
value={updateForm.Address}
error={Boolean(errors.Address) && Boolean(touched.Address)}
helperText={Boolean(touched.Address) && errors.Address}
/>
<Box height={14} />
<Field name="phoneNumber" type="number" as={TextField} variant="outlined" color="primary" label="Phone Number" fullWidth
error={Boolean(errors.phoneNumber) && Boolean(touched.phoneNumber)}
helperText={Boolean(touched.phoneNumber) && errors.phoneNumber}
onChange={handleUpdateFieldChange}
value={updateForm.phoneNumber}
/>
<Box height={14} />
<Field name="Email" type="email" as={TextField} variant="outlined" color="primary" label="Email" fullWidth
error={Boolean(errors.Email) && Boolean(touched.Email)}
helperText={Boolean(touched.Email) && errors.Email}
onChange={handleUpdateFieldChange}
value={updateForm.Email}
/>
<Box height={16} />
<Button type="submit" variant="contained" color="primary" size="large" disabled={!dirty || !isValid} >Update Customer</Button>
</Form>
)}
</Formik>
</div>)}
{!updateForm._id && <div>
<h2>Create Customer:</h2>
<Box height={20} />
<Formik initialValues={formik.initialValues}
validationSchema={object({
Name: string().required("Please enter a name").min(3, "Name is too short"),
Address: string().required("Please enter an address").min(3, "Address is too short"),
phoneNumber: number().required("Please enter a phone number").min(4, "Phone number is too short"),
Email: string().required("Please enter an email").email("Invalid email"),
})}
onSubmit={(values, formikHelpers) => {
console.log(values);
formikHelpers.resetForm();
}}
>
{({ setFieldValue, errors, isValid, touched, dirty,handleBlur,handleSubmit}) => (
<Form onSubmit={createCustomer} >
<Field as={TextField} name="Name" type="name" variant="outlined" color="primary" label="Name" fullWidth
onChange={updateCreateFormField} value={createForm.Name}
error={Boolean(errors.Name) && Boolean(touched.Name)}
helperText={Boolean(touched.Name) && errors.Name}
/>
<Box height={14} />
<Field name="Address" type="Address" as={TextField} variant="outlined" color="primary" label="Address" fullWidth
error={Boolean(errors.Address) && Boolean(touched.Address)}
helperText={Boolean(touched.Address) && errors.Address}
onChange={updateCreateFormField} value={createForm.Address}
/>
<Box height={14} />
<Field name="phoneNumber" type="number" as={TextField} variant="outlined" color="primary" label="Phone Number" fullWidth
error={Boolean(errors.phoneNumber) && Boolean(touched.phoneNumber)}
helperText={Boolean(touched.phoneNumber) && errors.phoneNumber}
onChange={updateCreateFormField}
value={createForm.phoneNumber}
/>
<Box height={14} />
<Field name="Email" type="email" as={TextField} variant="outlined" color="primary" label="Email" fullWidth
error={Boolean(errors.Email) && Boolean(touched.Email)}
helperText={Boolean(touched.Email) && errors.Email}
onChange={updateCreateFormField}
value={createForm.Email}
/>
<Box height={16} />
<Button type="submit" variant="contained" color="primary" size="large" disabled={!dirty || !isValid} >Create Customer</Button>
</Form>
)}
</Formik>
</div>}
<Box height={40} />
</div>
)
Sandbox
Simply use Formik's handleChange event and values. You don't need to create custom functions or states to change values.
Working example here
Also, if you want to have custom function to have inside onChange you can use formik's setFieldValue.
More information related to formik events https://formik.org/docs/api/formik
I would love to have a search bar in "Select" component from the library native-base in react-native.
I have tried adding a "TextInput" component inside the "Select" component. In UI it aligns perfectly, but when I click on the "TextInput" it gets selected and the list drops down.
Following is the code I tried:
<Select
w={w}
h={h}
variant="outline"
selectedValue={selectedValue}
minWidth="200"
// borderColor={primaryColor}
accessibilityLabel={accessibilityLabel?accessibilityLabel: "Choose Service" }
placeholder={placeholder?placeholder: "Choose Service"}
_selectedItem={{
bg:"coolGray.200",
// endIcon: <CheckIcon size="5" />
}}
mt={1}
onValueChange={onValueChange}
>
<Input
placeholder="Search"
variant="filled"
width="100%"
h={heightPercentageToDP("6%")}
borderRadius="10"
py="1"
px="2"
borderWidth="0"
/>
{
data?.map(item => {
return(
<Select.Item
label={item.label}
value={item.value}
/>
)
})
}
</Select>
Select box of native base has prop _actionSheetBody, it contains IFlatListProps so you can use ListHeaderComponent in there. So can use this way.
You use a state to save search value:
const [searchValue, setSearchValue] = React.useState<string>('');
Edit select box
`
<Select w={w} h={h} variant="outline" selectedValue={selectedValue}
minWidth="200"
accessibilityLabel={accessibilityLabel?accessibilityLabel: "Choose Service" }
placeholder={placeholder?placeholder: "Choose Service"}
_selectedItem={{
bg:"coolGray.200",
// endIcon: <CheckIcon size="5" />
}}
mt={1}
onValueChange={onValueChange}
_actionSheetBody={{
ListHeaderComponent: <FormControl px={3} mb={3}>
<Input
px={15}
py={2}
fontSize={16}
value={searchValue}
placeholder=""
_focus={{ bg: colors.white['50'], borderColor: 'darkBlue.600' }}
type='text'
onChangeText={(value: string) => {
setSearchValue(value);
}}
/>
</FormControl>
}}
>
<Input
placeholder="Search"
variant="filled"
width="100%"
h={heightPercentageToDP("6%")}
borderRadius="10"
py="1"
px="2"
borderWidth="0"
/>
{
(data && data.length)?data.filter((item)=>{
// you filter with searchValue
return true;
}).map(item => {
return(
<Select.Item
label={item.label}
value={item.value}
/>
)
})
}
</Select>
I have a form where and I am validating the form using Formik, I want to multiply the value on the quantity input and unit cost input when there's an input and then automatically display it in the total input. I'm using Formik + Chakra_UI.
<Formik
initialValues={{
productName: "",
productNumber: "",
unitCost: 0,
totalCost: 0,
quantity: 0,
}}
>
{({ values }) => (
<Form>
<Field name="productName">
{() => (
<Grid templateColumns="repeat(2, 1fr)" gap={5}>
<Box>
<FormControl>
<FormLabel htmlFor="productName">Product Name:</FormLabel>
<Input id="productName" placeholder="Product Name" />
{/* <FormErrorMessage>{form.errors.name}</FormErrorMessage> */}
</FormControl>
</Box>
<Box>
<FormControl>
<FormLabel htmlFor="productNumber">
Product Number:
</FormLabel>
<Input id="productNumber" placeholder="Product Number" />
{/* <FormErrorMessage>{form.errors.name}</FormErrorMessage> */}
</FormControl>
</Box>
<Box>
<FormControl>
<FormLabel htmlFor="quantity">Quantity:</FormLabel>
<Input id="quantity" placeholder="Quanity" />
{/* <FormErrorMessage>{form.errors.name}</FormErrorMessage> */}
</FormControl>
</Box>
<Box>
<FormControl>
<FormLabel htmlFor="unitCost">Unit Cost:</FormLabel>
<Input id="unitCost" placeholder="Unit Cost" />
{/* <FormErrorMessage>{form.errors.name}</FormErrorMessage> */}
</FormControl>
</Box>
<Box>
<FormControl>
<FormLabel htmlFor="totalCost">Total Cost:</FormLabel>
<Input id="totalCost" placeholder="Total Cost" />
{/* <FormErrorMessage>{form.errors.name}</FormErrorMessage> */}
</FormControl>
</Box>
</Grid>
)}
</Field>
<Button isFullWidth mt={6} colorScheme="green" type="submit">
Submit
</Button>
</Form>
)}
</Formik>
To keep code for state management shorter you could just remove totalCost from values and compute it on use.
Updated code would look like this:
<Formik
initialValues={{
productName: "",
productNumber: "",
unitCost: 0,
quantity: 0,
}}
onSubmit={...}
>
{({ values }) => (
<Form>
<Grid templateColumns="repeat(2, 1fr)" gap={5}>
// ... other boxes stay same as before
<Box>
<FormControl>
<FormLabel htmlFor="totalCost">Total Cost:</FormLabel>
<Input id="totalCost" placeholder="Total Cost" value={values.quantity * values.unitCost} />
{/* <FormErrorMessage>{form.errors.name}</FormErrorMessage> */}
</FormControl>
</Box>
</Grid>
<Button isFullWidth mt={6} colorScheme="green" type="submit">
Submit
</Button>
</Form>)}
</Formik>
then you'll repeat same calculation for onSubmit.
Coult be also good idea to apply some rounding on as I assume you use it for currency value={values.quantity * values.unitCost}
maybe you could just simplify it to
<FormControl>
<FormLabel htmlFor="totalCost">Total Cost:</FormLabel>
<Box id="totalCost">{Math.round((values.quantity * values.unitCos + Number.EPSILON) * 100) / 100}</Box>
</FormControl>
rounding explained here: Round to at most 2 decimal places (only if necessary)
I'm following the docs of Antd and tried to use this piece of code from here antd dynamic form item:
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
<Form.Item
{...field}
name={[field.name, 'first']}
fieldKey={[field.fieldKey, 'first']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'last']}
fieldKey={[field.fieldKey, 'last']}
rules={[{ required: true, message: 'Missing last name' }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add field
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
But I have this error, when I add some rows, then delete some of them and finally submit, the validator keeps working even after I had deleted those rows.
Here is a demo that replicates my error.
https://codesandbox.io/s/quizzical-ride-m1pe6?file=/src/App.js
This is a bug from antd.
An issue was opened about that on their github.
https://github.com/ant-design/ant-design/issues/27576
And the associate PR :
https://github.com/react-component/field-form/pull/213
A fix has been merged last week. Normally, the next release will include the fix.