How can I normalize the value of the redux-form field when onBlur is triggered. I tried the following, but it didn't seem to work:
const normalizeAmount = (node) => {
const newValue = node.target.value;
return `--${newValue}--`;
};
render() {
const { handleSubmit, pristine, invalid, submitting, error, blur } = this.props;
return (
<form onSubmit={handleSubmit(submit)}>
<div name="form-container">
<Field
name="foo"
component={CustomInput}
onBlur={blurValue => blur(normalizeValue(blurValue))}
/>
...
);
Solved this by moving onBlur to CustomInput component, where I add
return (
<div>
<input
...
onBlur={value => props.input.onBlur(normalizeValue(value))}
/>
</div>
);
And in the form component the field will just have:
<Field
name="foo"
component={CustomInput}
/>
actually normalize is used before any changing in field so it's used before onBlur event, but you are trying to use it in wrong way.
You can use something like this and to allow user to enter only numbers:
<Field component={TextInputField}
onBlur={value => this.doWhatEverYouNeed(value)}
normalize={value => value.replace(/[^\d]/g, '')}
/>
More details about normalize you can find on https://redux-form.com/8.2.2/examples/normalizing/
Related
I am currently using VeeValidate 4 on my vue3 design-system, I am encountering some difficulties in triggering the correct way the rules both in fields and form and I am trying to figure out why in an external project using the same library.
This is how i used useField on my-input
const {
value: modelValue,
handleChange,
handleBlur,
} = useField(name.value, undefined, {
initialValue: props.modelValue,
validateOnValueUpdate: false
})
notice that validateOnValueUpdate is set to false, but it keeps triggering validation on input.
This is instead my situation on the project i am currently working on.
I specified in a file my global rules using defineRule the 'required' and 'email' one.
function notValid(values) {
console.log('not valid', values);
}
const formValidate = (values) => {
console.log('submitting', values)
}
const schema = {
username: 'required',
email: 'required|email',
}
<Form #submit="formValidate" :validation-schema="schema" #invalid-submit="notValid">
<Field name="username" rules="required" v-slot="{ field, errorMessage, meta }">
<my-input label="username"
class="pb:1"
v-bind="field"
:modelValue="initialValues.username"
:error-message="errorMessage"
:error-status="!meta.valid && meta.touched && meta.dirty || !meta.valid && meta.touched && meta.validated"
/>
</Field>
<Field name="email" rules="required|email" v-slot="{ field, errorMessage, meta }">
<my-input label="email"
class="pb:1"
v-bind="field"
:modelValue="initialValues.email"
:error-message="errorMessage"
:error-status="!meta.valid && meta.touched && meta.dirty || !meta.valid && meta.touched && meta.validated"
/>
</Field>
<f-button value="submit" type="tertiary"/>
</Form>
In this actual case it's working but not the way i want it to do, triggering errors inside the component which allow me to see the error status on my input.
Using the props in this way destroys the whole purpose of using rules as global beacause I want them to also apply the field and not only the form.
I'm struggeling with making my Formik form work. Here a part of my code:
import { Formik, Form as FormikForm, Field } from "formik";
import {TextField} from "#mui/material";
<Formik
initialValues={{
name: "test",
}}
onSubmit={this.onSubmit}
>
{({ values,handleChange }) => (
<FormikForm>
<Field
component={TextField}
name="name"
label="Name"
fullWidth
></Field>
<Button
color="success"
variant="contained"
type="submit"
onClick={() => {
console.log(values);
}}
>
Erstellen
</Button>
</FormikForm>
)}
</Formik>
It seems like its having trouble connecting the values state to the Field value, since the initialValue isn't displayed in the names field. When i submit, it logs {name: 'test'} to console, no matter what i enter in the input field.
Edit 1:
I also have a very similar form that works. The only difference between the two that i can think of is: the working one is a .jsx while this one is .tsx. Dont know if that has anything to do with it.
According to the Field documentation you need to spread the field and pass it to the component like this:
import { Formik, Form as FormikForm, Field } from "formik";
import { Button, TextField } from "#mui/material";
const MuiTextField = ({ field, form, ...props }) => {
return <TextField {...field} {...props} />;
};
const MyComponent = () => {
return (
<Formik
initialValues={{
name: "test"
}}
onSubmit={(values) => {
console.log(values);
}}
>
{({ values, handleChange }) => (
<FormikForm>
<Field
component={MuiTextField}
name="name"
label="Name"
fullWidth
></Field>
<Button
color="success"
variant="contained"
type="submit"
onClick={() => {
console.log(values);
}}
>
Erstellen
</Button>
</FormikForm>
)}
</Formik>
);
};
export default MyComponent;
You can take a look at this sandbox for a live working example of this solution.
Like Ahmet pointed out, the problem seems to be that the field prop needs to be spread (see his answer). However i found, that using as instead of component works too and doesnt need spreading.
<Field
as={TextField}
name="name"
label="Name"
fullWidth
></Field>
I have a form and 2 submit flows: draft and detailed.
There is no validation for submitting the draft.
Some of the fields are required for detailed submitting. One of them is "description".
Invalid form:
Valid form:
Validation works properly and "description" field gets marked as "invalid" if empty:
<Field
component="textarea"
name="description"
maxLength={2048}
validate={[required()]}
label={<FormattedMessage id={messageIds['entity.ticket.field.description']} />}
/>
But at the same time, I can't submit this form as "draft" when the description is empty.
Shortened code:
function TicketFormComponent(props: AllProps) {
const handleDraftTicketSubmit = props.handleSubmit((values) => {
props.onTicketCreate({
...values,
status: 'Draft',
});
});
const handleActiveTicketSubmit = props.handleSubmit((values) => {
props.onTicketCreate({
...values,
status: 'Open',
});
});
return (
<Form onSubmit={handleActiveTicketSubmit}>
<Field
component={TextInput}
name="subject"
maxLength={255}
label={<FormattedMessage id={messageIds['entity.ticket.field.subject']} />}
/>
<h3><FormattedMessage id={messageIds['entity.ticket.field.description']} /></h3>
<Field
component="textarea"
name="description"
maxLength={2048}
validate={[required()]}
label={<FormattedMessage id={messageIds['entity.ticket.field.description']} />}
/>
<Button
data-aqa="submitDraftTicket"
disabled={props.submitting || !props.initialValues.selectedTopic}
onClick={handleDraftTicketSubmit}
className={props.classes.saveDraftButton}
>
<FormattedMessage id={messageIds['entity.ticket.field.saveAsDraft']} />
</Button>
<Button
data-aqa="submitActiveTicket"
disabled={props.submitting || props.invalid}
onClick={handleActiveTicketSubmit}
variant="contained"
color="primary"
>
<FormattedMessage id={messageIds['entity.ticket.field.createNewTicket']} />
</Button>
</Form>
);
}
Expected result:
Form values are submitted with "Draft" status further through Redux flow
Actual result:
form values are not submitted, instead, I get 2 redux actions:
{ type: '##redux-form/TOUCH' }
{ type: '##redux-form/SET_SUBMIT_FAILED', error: true }
I'm seeing two possibles approaches:
1. Remove Field level validation (validate={[required()]}) and validate the Form with SyncValidation:
const validate = values => {
const errors = {}
const {isDraft, ...rest} = values
if(isDraft) {
// draft validations
} else {
// other validations
}
return errors
}
export default reduxForm({
form: 'myForm', // a unique identifier for this form
validate, // <--- validation function given to redux-form
})(MyForm)
2. Add the Field validation conditionally:
<Field
name="description"
validate={!isDraft && [required()]}
/>
But here you should change your submission flow. Rather than submitting the Form on 'Save draft' or 'Create ticket' buttons, you have to set a state isDraft variable firstly and later submit the form.
Otherwise, submitting the Form on a button click, the conditional Field validation won't be applied, because the Form will be already submitted.
Here, the use-case is very similar to yours. So you can take inspiration of it too.
Given a custom component:
export const RenderDropDown = (props) => {
const {values, input, label, type, meta: {touched, error, active}} = props;
var options = function (value, index) {
return <Option key={index}
value={value} />
};
return(
<div>
<select {...input}>
<option value="">--- Select a nationality ---</option>
{values.map(options)}
</select>
</div>
)
}
Wrapped in a Redux-form Field component
<Field name="nationality" component={RenderDropDown} values={props.nationalities}/>
I want that when select fires "onChange" event, a custom action is dispatched besides the Redux-form action (AKA ##redux-form/change)
How I can achieve this?
I tried to pass by props the function to call and set an onChange handler on <select> tag but this overrides the redux-form onChange.
Any help appreciated!
You need to call the redux-form onChange manually in your own custom onChange to update the redux-form value. Like so:
const { onChange, ... } = props;
...
<select onChange={(e) => {
const val = e.target.value
// whatever stuff you want to do
onChange(val)
}} />
I notice in a textarea field, sync-error handling doesn't behave like an input field. For example, after displaying a sync-error on a form's input field the error happily disappears when I start typing in the field. On a textarea field, the sync-error just sits there when I start typing (or when I leave the field).
Basically, onBlur is not setting touched to true when I exit the field.
What extra things should I consider when dealing with sync-error handling on a textarea field in a Redux form?
redux-form ^6.x.x changed a lot of its implementation compared to v4 and v5
If you post you sample code, I can help you better though.
I think what u meant, normal field works fine but field
That means your reduxForm(...) works fine, there might be the problem in your error handler function or field rendering function.
I leave simple code example
// validate
const validate = (values) => {
const errors = {};
if(!values.title) {
errors.title = 'title require';
}
if (!values.categories) {
errors.categories = 'categories require';
}
if (!values.content) {
errors.content = 'content require';
}
return errors;
};
// renderField
const renderField = ({ input, label, type, textarea, meta: { touched, error, warning, invalid } }) => {
const textareaType = <textarea {...input} placeholder={label} type={type} className={`form-control ${touched && invalid ? 'has-danger' : ''}`}/>;
const inputType = <input {...input} placeholder={label} type={type} className={`form-control ${touched && invalid ? 'has-danger' : ''}`}/>;
return (
<div>
<label>{label}</label>
<div>
{textarea ? textareaType : inputType}
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
);
};
// React render
<form onSubmit={handleSubmit}>
<Field name="title" component={renderField} type="text" label="Title" />
<Field name="categories" component={renderField} type="text" label="Categories" />
<Field name="content" component={renderField} type="text" label="Content" textarea={true} />
<button type="submit" disabled={pristine || submitting} className="btn btn-primary">Submit</button>
</form>
reduxForm({
form: 'TestComponent', // Name of the form
validate // <--- validation function given to redux-form
})(TestComponent)