Conditionally validating Formik field with YUP via a state hook? - react-hooks

I have searched around, not sure if this is possible. Essentially I am wanting to validate a formik form with YUP using a state hook that is not a form value.
validationSchema={Yup.object({
comments: Yup.string()
.when(approvalState, {
is: false,
then: Yup.string().required('Comments are required when denying an approval.'),
}),
})}
Here is the form field, it is essentially a text box:
Here is the submit button, which changes the state value to true:
<button className='buttonPrimary' type='submit' onClick={()=> setApprovalState(true)} disabled={formik.isSubmitting}>Approve</button>
Here is the other button which also submits, changes the state value to false:
<button className={`buttonSecondary ${styles.marginRight}`} type='submit' onClick={()=> setApprovalState(false)} disabled={formik.isSubmitting}>Deny</button>
The state value updates fine, any way to conditionally validate the textbox as required based on a piece of state?

Your solution will work if approvalState is in values of formik:
Yup.object({
comments: Yup.string()
.when('approvalState', { // Change approvalState to string
is: false,
then: Yup.string().required('Comments are required when denying an approval.'),
}),
})
Alternatively you can do:
Yup.object({
comments: approvalState
? Yup.string()
: Yup.string().required('Comments are required when denying an approval.')
})

you can use state hook and render conditionally. please have a look at this thread https://stackoverflow.com/a/54919273/10154990

Related

parsley.js prevents submit after failing ajax validation has been corrected

i have a form where i need to see if an email address entered into the form is already in a database. this check needs to be performed conditionally based on the value of another field in the form.
here is the form field:
<input type="email" value="" class="form-control" name="email_bill" id="email" required data-parsley-type="email" data-parsley-registered="1" data-parsley-trigger="focusout submit">
and here's the validator code:
Parsley.addValidator('registered', {
validateString: function(value) {
if ($('input[name="d_type"]:checked').val() == 'S') {
return $.ajax({
type: "POST",
url: "/is_registered.html",
data: $('form').serialize()
});
} else {
var parent = $('#email').closest('div');
var this_id = $('#email').attr('data-parsley-id');
$(parent).removeClass('has-error').addClass('parsley-success');
$(this_id).remove();
return true;
}
},
messages: {en: "Someone has already registered with the Email address you entered"}
});
the server code is trivial and returns a '200 OK' header if the address isn't in the database and a '404 Not Found' if it is. that works.
i followed the example in the parsley.js custom validator example for the simple ajax request.
what happens is: if i enter a 'registered' address, i get the appropriate error message. if i then go and modify that address to one i know is NOT registered and tab or mouse out to the next field, the error message goes away, BUT the submit button doesn't work. to further complicate the situation, if i load and fill out a form with a 'non-registered' address, the submit button doesn't work either. it appears that execution of the custom validator disables submit upon entry.
i've played with this for hours, trying all sorts of event manipulation, but nothing works.
i should point out that if the checked value of d_type (see field definition above) is NOT 'S', then everything works as expected.
i am totally baffled as to why following the documentation results in failure.
as it turns out, this was not a problem with parsley at all.
a colleague created the form and the submit button had an id of "submit' which could not allow parsley to resolve the submit handler. under those circumstances, apparently parsley blocks the submit.
who knew?
well now, we know.....

Async validate single redux form field, but not on submit

Question
How can one build a async validation on a single form field that also can trigger filed level errors, but without using a asyncValidation that also triggers onSubmit?
Background to question
I have a Redux form where the user can choose to:
a) Input adress details by hand
b) Fill in their social security number and fetch all details from a API and get the redux form fields prefilled (wohoo, time saved!)
c) Being 100% anonymous by checking a checkbox which removed all personal details fields.
Given this kind of setup a asyncValidator that triggers onSubmit and onBlur seems tricky, i don't want them to trigger more than once if a get the desired details and the form get pre-filled. But if user submits a invalid social security number I want to trigger a field error.
if you just want to validate a single field, you can pass a prop validate
with an array of validation functions, that will return false if its valid, and a string error if invalid based on your logic. and will prevent submission, or will show an error on submittion.
const required = (value) => (value ? false : 'Required')
const isSSN = (value) => (willBeTrueIfSSN ? false : 'Invalid SSN')
<Field name="ssn" validate={[isSSN, required]} {...rest} />
some good examples in the redux-form docs:
https://redux-form.com/7.2.3/examples/fieldlevelvalidation/

Resetting a form generated server-side in vue.js

I am rendering a form with Blade, Laravel's server-side templating language. The default values for the form elements are assigned by Blade. There is no JavaScript involved until now. Now I want to implement a reset button.
When a user presses the reset button the form should be cleared. A simple HTML reset button is not sufficient as it would not reset the "value=something" default values to "null".
In other words:
<input type="text" name="fullname" value="John Doe">
is supposed to be
<input type="text" name="fullname" value="">
after the user pressed the reset button.
With JQuery I would do something like this:
$("body").find('form').find('input').val('');
How can I do it with vue.js? Adding av-model and setting the v-model properties to null interferes with the server side default values...
In general: would you suggest to add a DOM manipulating lib to the application for such "hybrid" use cases where vue.js does not control the data?
If you plan on using vue, forget about altering the dom, vue works around states so imagine your input is like this
<input type="text" v-model="test_input">
and when you change the variable test_input the input automaticly changes its value, so just set it to empty in a method.
<button #click="clear_form"> Clear </button>
<script>
export default {
data() {
return {
test_input : ''
}
},
methods:{
clear_form(){
this.test_input = '';
}
}
}
</script>
I ended up writing a reset-form button component. In this component I use plain Javascript to get all input, select, ... fields of the form identified by an id and reseted the values to ''.
I took this option as it was the fastest way to reset the form and I don't have to change anything (e.g. add props, change ajax calls) if my form changes.

how to clear validation errors for Angular Material mat-error

I'm trying to reset a form after I've added a value.
Form Code Snippet
<form [formGroup]="addAttributeForm" fxLayout="column">
<mat-form-field>
<input matInput formControlName="title" placeholder="Title" required>
<mat-error>This field is required</mat-error>
</mat-form-field>
</form>
In the component
onSubmit(form: FormGroup) {
// do work
form.reset();
}
What I'm observing:
The form values are set to empty.
But the validation messages are still displayed from mat-error.
I've tried form.markAsPristine(), form.markAsUntouched() and combining all three.
How can I reset the form so the mat-error is not displayed?
The form group has no "knowledge" about whether the actual HTML form has been submitted or not. It only keeps track of the form values/validity/enabled state. So resetting the form group does reset the values but not any state regarding whether the form has been submitted.
To do this, you need to get a hold of the FormGroupDirective and call resetForm() on it.
Form Code Snippet
<form [formGroup]="addAttributeForm" fxLayout="column">
<!-- ... -->
</form>
In the component
#ViewChild(FormGroupDirective) formDirective: FormGroupDirective;
onSubmit(form: FormGroup) {
// do work
this.formDirective.resetForm();
}
This solution works with me.
You need to do next:
formReset(form: FormGroup) {
form.reset();
Object.keys(form.controls).forEach(key => {
form.get(key).setErrors(null) ;
});
}
This reset the form and clear all error.
The only way I've been able to successfully do this is by setting a flag to hide the form when you want to reset it and then using a timeout to set that flag back to true. As far as I know, there is no built-in way to do this yet.
showForm = true;
reset(): void {
this.showForm = false;
setTimeout(() => this.showForm = true);
}
And then in the HTML on the form element use *ngIf="showForm".

Can I use Asynchronous Blur Validation with redux-form on a hidden input field?

I have a redux-form specific question.
I have a hidden input in my form:
<input type="hidden" {...field} />
And would like to perform some asynchronous validation by making a request to the server. What is the most elegant/appropriate way to achieve this? I've been looking into Asynchronous Blur Validation, which seems like the right thing except for the fact that a hidden input doesn't seem to be able to trigger a blur event.
EDIT
I managed to get what I wanted by hooking up the following middleware code to Redux:
if (action.type === ActionTypes.MY_EVENT) {
MyModule.asyncValidate({
myField: store.getState().form.myForm.myField.value,
}).catch((error) => {
store.dispatch(stopAsyncValidation('myForm', error));
});
}
Yeah, asking how to do something when a hidden input blurs is like asking how to do something when your water is dry.
The concept of hidden inputs doesn't really exist in redux-form, as hidden inputs are based on the ye olde HTML way of submitting forms. A "hidden input" in redux-form is just a field that appears in the fields list (so it will be submitted), but that is not rendered to any input. I do this for primary keys, where my fields list is ['id', 'name', 'email'], and I only render inputs for name and email, but all three get submitted.
I think what you want is for your async validation to fire when a rendered (non-hidden) field is blurred, and for all of the fields, even the ones that are not rendered, to be passed to the async validation function. With the example above, something like this would work:
reduxForm({
form: 'myForm',
fields: ['id', 'name', 'email'],
asyncValidate: doServerSideAsyncValidation,
asyncBlurFields: ['name', 'email'] // both rendered fields will trigger async validation
})

Resources