How to submit invalid form in redux-form? And is it even possible? - redux-form

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.

Related

Which is the correct way to trigger both form and field validation using VeeValidate 4 custom-input from an external package?

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.

React formik form onsubmit event callings many times?

import { Formik, Form, Field } from "formik";
import { Button } from "antd";
const AddUser = () => {
const initialValues = {
name: "",
};
return (
<>
<Formik
initialValues={initialValues}
onSubmit=(values) => {
alert("hi");//calling mamy times
Here added api call (post method)
}}
>
{({ isValid, submitForm, isSubmitting, values }) => {
return (
<Form>
<Field
name="name"
label="Name"
placeholder="Dataset Name"
/>
<Button
type="primar"
htmltype="submit"
loading=(props.addingdata) // this is my reducer state intial was false after post call request became true and success state value false
>
Add Dataset
</Button>
</Form>
);
}}
</Formik>
</div>
</>
);
};
export default AddUser;
I have simple formik form antd button I have used when click submit button post api calling twice and thrice even If I added loading property in button why its happening like this?

Sync validation to throw form level error (rather then field level)

In sync validation per - https://redux-form.com/7.1.2/examples/syncvalidation/ - I was trying to throw a form level error. Just like when we do throw new SubmissionError({ _error:'form level error from on submit' }), however I am not able to figure it out. I tried using the special _error key and even just error key like so:
reduxForm({
validate: values => ({ error:'form level??', _error:'form level alt??' })
})
However it is not setting FormProps error value. Does anyone know if this is supported or how to achieve this?
To pass form-level error to form after validation you should add _error property to object returned by redux-form config validate function.
The error will be passed as error prop to your form component.
For example:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
const ExampleForm = (props) => {
const { handleSubmit, pristine, submitting, error } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name</label>
<div>
<Field
name="name"
component="input"
type="text"
placeholder="Name"
/>
</div>
</div>
<div>Error: {error}</div>
<div>
<button type="submit" disabled={pristine || submitting}>Submit</button>
</div>
</form>
);
};
const validate = (values) => ({
_error: `form level error for firstName = '${values.name || ''}'!`,
});
export default reduxForm({
form: 'exampleForm',
validate,
})(ExampleForm);

Normalize redux-form field value onBlur

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/

Redux form textarea error handling

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)

Resources