Is it possible to achieve sth like this currently?
<Formik
initialValues={{ nested: { value: 'nested value' } }}
validationSchema={schema}
onSubmit={(values, { setSubmitting }) => {
handleSubmit(values);
setSubmitting(false);
}}
>
{() => (
<Form>
<Field name="nested.value" />
<button type="submit">Save</button>
</Form>
)}
</Formik>
I know I could use mapPropsToValues and mapValuesToPayload, but I find it really verbose and inconvenient for such a simple and common case. If I had deeply nested object, it would be even worse.
I tried to solve it in my custom component:
import React from 'react';
import { FormGroup, ControlLabel, FormControl, HelpBlock } from 'react-bootstrap';
import { get } from 'lodash';
const TextInput = ({ field, form: { touched, errors, values, setFieldValue }, ...props }) => (
<FormGroup
validationState={touched[field.name] && errors[field.name] ? 'error' : null}
>
<ControlLabel>{props.label}</ControlLabel>
<FormControl
type="text"
{...field}
{...props}
value={get(values, field.name)}
onChange={e => setFieldValue(field.name, e.target.value)}
/>
{touched[field.name] && errors[field.name] && <HelpBlock>{errors[field.name]}</HelpBlock>}
</FormGroup>
);
Please notice that I used _.get in value prop so reading is working for nested values. The problem is, that setFieldValue('nested.value', value) would add nested.value key to values object, instead of adjusting values.nested.value. The same problem I would have for setFieldError, setFieldTouched etc.
It seems that this library requires objects to be totally flat, but nested JSONs are really common. And flattening and "deflattening" in every form really kills productivity.
Merged in #207
@klis87 Thanks for sharing. How did you add bootstrap to formik. I dont even find any documentation online https://github.com/jaredpalmer/formik/issues/530
Can you please share. thanks you
@abelkbil You can integrate Formik with whatever you desire, you need to create a component which you then should pass to Field component prop. See https://codesandbox.io/s/jRzE53pqR as an official example how to do it.
First of all - thanks for your hard work!
@jaredpalmer Would be great to add also nested touched and errors proposal prototype.
Because now I'm using get from lodash for them like below:
touched={get(touched, field.name)}
Where field.name corresponds to the nested object (eg. 'address.city')
For anyone searching the web and finding this issue, what you are looking is documented at https://jaredpalmer.com/formik/docs/api/field#name
was this fixed at all in 2.x? the touched, errors stuff?
I believe some people will stall have trouble with touched and set errors.
This was my custom input, followed by example usage with Formik Field component.
PS: you should remove CurrencyInput parts, probably don't need them 😰
Replace the Comp part with your custom Input component.
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Input, CurrencyInput } from '../index';
const ConnectedInput = ({
field,
form,
required,
currency = false,
withOutErrorLabel,
...otherProps
}) => {
const { name, onChange, ...otherFieldProps } = field;
const { handleBlur, touched, values, errors, validateForm } = form;
const [, i18n] = useTranslation();
const dir = i18n.dir();
let Comp = Input;
const _handleChange = (eventOrValue) => {
if (!currency) {
form.setFieldValue(name, eventOrValue.target.value);
} else {
form.setFieldValue(name, eventOrValue.floatValue);
}
};
// Fixes persitant ERROR message on locale change.
useEffect(() => {
// Re-runs validation on direction change --> New translations for errors are set.
validateForm();
}, [validateForm, dir]);
let _touched = touched[name];
let error = errors[name];
// handles nested fields errors and touched properties.
if (
name.indexOf('.') > 0 &&
Object.keys(errors).length &&
Object.keys(touched).length
) {
const [parentObj, fieldName] = name.split('.');
error = errors[parentObj] && errors[parentObj][fieldName];
_touched = touched[parentObj] && touched[parentObj][fieldName];
}
if (currency) {
Comp = CurrencyInput;
}
return (
<Comp
id={name}
name={name}
invalid={touched[name] && !!errors[name]}
onChange={_handleChange}
onBlur={handleBlur}
value={values[name]}
label
error={error}
required={required}
touched={_touched}
withOutErrorLabel={withOutErrorLabel}
{...otherFieldProps}
{...otherProps}
/>
);
};
Example usage:
<Field
name='title.en'
required
component={ConnectedInput}
label={englishName}
placeholder={englishNamePlaceholder}
/>
Sorry for the messy parts, got a task 🤦♂️
Most helpful comment
I tried to solve it in my custom component:
Please notice that I used
_.getinvalueprop so reading is working for nested values. The problem is, thatsetFieldValue('nested.value', value)would addnested.valuekey tovaluesobject, instead of adjustingvalues.nested.value. The same problem I would have forsetFieldError,setFieldTouchedetc.It seems that this library requires objects to be totally flat, but nested JSONs are really common. And flattening and "deflattening" in every form really kills productivity.