react-hook-form validate that password match (failing) - react-hook-form

I am using react-hook-form to validate that my password and confirm password are the same. For some reason, the form elements aren't validating. I confirmed with a console.log that the reference and the confirm password are the same, but it is failing anyways. It all seems fine, where have I gone wrong?
Code
const { setValue, register, getValues, handleSubmit, watch, formState: { errors } } = useForm();
const password = useRef({});
password.current = watch("password", "");
const selectType = watch("type", '');
<IonItem >
<IonLabel position="floating">Password</IonLabel>
<IonInput type="password" {...register("password", { required: 'Password is required' })}/>
</IonItem>
<IonItem >
<IonLabel position="floating">Confirm Password</IonLabel>
<IonInput type="password" {...register("password_repeat", { validate: value => value === password.current || "The passwords do not match" })}/>
</IonItem>

The problem is that the interface for <IonInput /> is different from the one register expects. With the spreading of register you're linking an onChange handler to update RHF's form state of that field. <IonInput /> uses a change handler called ionChange instead.
As a rule of thumb you should use RHF's <Controller /> when working with external controlled components.
const { control, handleSubmit, watch, formState: { errors } } = useForm();
const password = watch("password", "");
<Controller
control={control}
name="password"
rules={{ required: "Password is required" }}
render={({ field }) => (
<IonItem>
<IonLabel position="floating">Password</IonLabel>
<IonInput
value={field.value}
ionChange={field.onChange}
ionBlur={field.onBlur}
onFocus={field.onFocus}
ref={field.ref}
type="password"
/>
</IonItem>
)}
/>
<Controller
control={control}
name="password_repeat"
rules={{
required: "Password is required",
validate: (value) =>
value === password|| "The passwords do not match"
}}
render={({ field }) => (
<IonItem>
<IonLabel position="floating">Password</IonLabel>
<IonInput
value={field.value}
ionChange={field.onChange}
ionBlur={field.onBlur}
onFocus={field.onFocus}
ref={field.ref}
type="password"
/>
</IonItem>
)}
/>

Related

react-hook-form and react-datetime: How to set the time to moment() from a button

I am using react-datetime inside a react-hook-form
I want the user to easily set the time to current time using a button Immediate. Instead of selecting the current time manually.
I am trying the below
const [currentDateTime, setcurrentDateTime] = useState(null);
<Controller
name="resetDateTime"
control={control}
required
render={({ field }) => (
<Datetime
onChange={setcurrentDateTime}
inputProps={{
placeholder: "MM-DD-YYYY HH:mm",
}}
value={currentDateTime}
viewMode="time"
/>
)}
/>
<Button color="primary" className="ml-1" onClick={() => setcurrentDateTime(moment())}>
{"Immediate"}
</Button>
The problem is onSubmit the react-hook-form I get resetDateTime = undefined
How to implement this properly. So I can use the Immediate button and also submit form and get the resetDateTime value
You're mixing RHF with your local state currentDateTime and are not linking the. field to RHF as you're missing to spread the field object to your <Datetime /> component.
The correct way would be to use RHF's setValue to update your resetDateTime field and get rid of the useState hook.
const { control, handleSubmit, setValue } = useForm();
<Controller
name="resetDateTime"
control={control}
required
render={({ field }) => (
<Datetime
{...field}
inputProps={{
placeholder: "MM-DD-YYYY HH:mm",
}}
viewMode="time"
/>
)}
/>
<Button color="primary" className="ml-1" onClick={() => setValue("resetDateTime", moment())}>
{"Immediate"}
</Button>

Issue in react final form

I am using react final form for validation purpose for login page which has forgot password and register link as well, now when I am clicking forgot password or register link ,it should not trigger any validation even though I am not filling my user name and password .I have tried t keep forgot password and register link away from tag but it is still triggering the validation on click of forgot password and register link .It should only trigger the validation when I m hitting submit button.
It should not ask to validate the form when I am clicking on any hyper link on the page as hyperlinks does not have any validations.
Here is the code sample
loginPage = () => {
const {t: translate} = this.props;
const {
match: {
params: {
authUrlKey = ''
} = {},
} = {},
} = this.props;
return (
<Form
onSubmit={ (values)=> this.validateUserCredentials(values)}
render={({ handleSubmit}) => (
<form onSubmit={handleSubmit}>
<button className="hidden" type="submit"/>
<h1 className="hw-block--pb">{translate('login.heading')}</h1>
<p className="hw-text-lead hw-block--pb-small">{translate('login.text')}</p>
{ this.state.description !=='' && <p className="hw-text-lead hw-block--pb-small">{this.state.description}</p> }
<div className="hw-grid">
<div className="hw-grid__item hw-one-whole hw-medium--one-fifth hw-large--one-sixth">
<label className="hw-label">{translate('login.landcode')}
<Field name="landcode" component={Dropdown} options={getCountryList()} onOptionSelect={this.onCountrySelect}/>
</label>
</div>
<div className="hw-grid__item hw-one-whole hw-medium--four-fifths hw-large--five-sixths">
<label className="hw-label">{translate('login.mobileNumber')}
<Field type="text" component={InputType}
validate={composeValidators(mobileNumberRequired, validMobileNumberWithISDCode)}
placeholder={translate('login.mobileNumberPlaceHolder')} name="phoneNumber"/>
</label>
</div>
</div>
<label className="hw-label">{translate('login.password')}
<Field type="password" component={InputType} validate={passwordRequired} placeholder={translate('login.passwordPlaceHolder')} name="password"/>
</label>
<Link className="hw-link" to={{ pathname: '/password/reset', state: {authUrlKey} }}>{translate('login.forgotPassword')}</Link>
<ErrorInfo error={this.state.error} errorMessage={this.state.errorMessage} translate={translate}/>
<div className="hw-block hw-block--mt-small">
<div className="hw-grid">
<div className="hw-grid__item hw-small--one-whole hw-medium--one-quarter hw-block--mb-smaller">
<button className="hw-button hw-button--primary hw-button--full" type="submit">{translate('login.loginButton')}</button>
</div>
<div className="hw-grid__item hw-one-whole hw-medium--three-quarters hw-block--mt-smaller">
<Link className="hw-link"
to={{ pathname: '/register', state: {authUrlKey} }}>{translate('login.registerButton')}</Link>
</div>
</div>
</div>
</form>)}
/>
)}
validations function used in code
export const validMobileNumberWithISDCode = (fieldValue='') => {
const value = trimValue(fieldValue);
const regex1 = /^\+?((45)|(46)|(47))?( )?\d{8,10}$/
return (regex1.test(value))? undefined : message[root.lang].validMobileNumber;
}
export const validMobileNumber = (fieldValue='') => {
const value = trimValue(fieldValue);
const regex1 = /^\d{8,10}$/;
return (regex1.test(value))? undefined : message[root.lang].validMobileNumber;
}
export const mobileNumberRequired = (fieldValue='') => {
const value = trimValue(fieldValue);
return value ? undefined : message[root.lang].mobileNumberRequired;
}
export const passwordRequired = (fieldValue='') => {
const value = trimValue(fieldValue);
return value ? undefined: message[root.lang].passwordRequired;
}
export const required =(fieldValue)=> {
const value = trimValue(fieldValue);
return value ? undefined : message[root.lang].required;
}```
validateUserCredentials -> This function does not contains any validation.It is used to retrieve form values and send it to server
React Final Form calls your validation function on every value change in the form, to ensure that the form validity is always up to date. Since you did not include the code for your validation function, I cannot ascertain what you are attempting to do. Your validation function should be very cheap to run (e.g. required fields, value length, etc.). The actual authentication should happen on submit.

Automatic "for" and "id"

Because we cannot assume our components are singletons: I am trying to automatically handle giving htmlFor to a label and field. We see below I use useMemo and lodash uniqueId to memoize a unique id for the form on initial render. I had to give useMemo and empty array as second argument so it never re-calculates the id. Is there some automated way to handle this in final-form?
import React, { useMemo } from 'react';
import { Form, Field } from 'react-final-form';
import { uniqueId } from 'lodash';
function TaskForm() {
const id = useMemo(() => uniqueId('_form'), []);
const getFor = name => name + id;
return (
<>
<h3>Create a task</h3>
<Form onSubmit={onSubmit}>
{({ handleSubmit, pristine, invalid, ...rest }) => {(
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor={getFor('firstName')}>First Name</label>
<Field name="firstName" id={getFor('firstName')} component="input" placeholder="First Name" />
</div>
<button type="submit" disabled={pristine || invalid}>Submit</button>
</form>
)}}
</Form>
</>
);
}

Knockout-Validation Show Template before input

I have a simple JSFiddle example http://jsfiddle.net/b625zeL5/6/
<script>
ko.validation.init({
registerExtenders: true,
messagesOnModified: true,
insertMessages: false,
parseInputAttributes: true,
messageTemplate: 'errorTemplate',
decorateInputElement: true,
errorElementClass: 'error'
}, true);
var ViewModel = function(){
this.email = ko.observable("")
.extend({ required: true })
.extend({ email: true });
this.password = ko.observable("")
.extend({ required: true });
};
var viewModel = new ViewModel();
viewModel.errors = ko.validation.group(viewModel);
ko.applyBindings(viewModel);
</script>
<form>
<span data-bind="validationMessage: email"></span>
<input type="text" id="email" data-bind="value: email, validationElement: email, valueUpdate:'keyup'" /> <br/>
<span data-bind="validationMessage: password"></span>
<input type="text" id="password" data-bind="value: password, validationElement: password, valueUpdate:'keyup'"/>
</form>
<script type="text/html" id="errorTemplate">
Error: <span data-bind="validationMessage: field">X</span>
</script>
As you can see - I disabled insertMessages because I need error messages to show before input field. Thus I added span with "data-bind="validationMessage: email"" before each text input.
I defined in validation config
messageTemplate: 'errorTemplate'
but error messages still plain text. How can I get messageTemplate to work?
Because you turned off insertMessages, knockout validation won't use your error message template and it will use what you inserted above each field.
You have two options:
For each observable that has a validation, add a custom error message.
Example 1:
this.password = ko.observable("")
.extend({ required: {
params: true,
message: "Error: This is required"
}
});
Change your error template to something like this:
Example 2:
<script type="text/html" id="errorTemplate">
Error: <span data-bind="validationMessage: error_field"></span>
</script>
.. and inside the form, you can call the template like:
<form>
<!-- ko template: { name: 'errorTemplate', data: { error_field: email } }-->
<!-- /ko -->
<input type="text" id="email" data-bind="value: email, validationElement: email, valueUpdate:'keyup'" /> <br/>
...
...
see jsfiddle here with example 2 in action : http://jsfiddle.net/mhgv48e8/
Hope it helps :)

Validate on Blur

I've created a JSFiddle to help demonstrate my question: http://jsfiddle.net/jeffreyrswenson/CrYWn/5/
Here's what I'd like to see:
Messages should not appear when page loads.
Messages should appear when submit button is pushed.
Messages should appear after input value is changed and user leaves element. (Tabs or clicks to next field)
Messages should appear after user leave an input without changing.(For example a field is required and the user tabs through the field, but doesn't enter a value. I'd like the validation message to appear when this happens.)
The first four work as I'd expect. Is the last item possible and if so, what do I need to change to enable that behavior?
HTML:
<label>First name:
<input data-bind='value: firstName' />
</label>
<br/>
<label>Last name:
<input data-bind='value: lastName' />
</label>
<br/>
<button type="button" data-bind='click: submit'>Submit</button>
<br/>
<span data-bind='text: errors().length'></span> errors
ViewModel:
var viewModel = function () {
ko.validation.configure({
decorateElement: true,
registerExtenders: true,
messagesOnModified: true,
insertMessages: true,
parseInputAttributes: true,
messageTemplate: null
});
this.firstName = ko.observable().extend({
required: true
});
this.lastName = ko.observable().extend({
required: true,
pattern: {
message: 'Hey this doesnt match my pattern',
params: '^[A-Z0-9]+$'
}
});
this.submit = function () {
if (this.errors().length == 0) {
alert('Thank you.');
} else {
this.errors.showAllMessages();
}
};
this.errors = ko.validation.group(this);
};
You just need to use the standard valueUpdate option of the value binding where you can specify additional events to trigger your property change and with that the validation.
So you just need to add the valueUpdate: "blur" setting on your bindings:
<label>First name:
<input data-bind='value: firstName, valueUpdate: "blur"' />
</label>
<br/>
<label>Last name:
<input data-bind='value: lastName, valueUpdate: "blur"' />
</label>
Demo JSFiddle.
In my case, I needed the value to update after key down because I was making some fields visible if the input had a value. I wanted the underlying value to update but didn't want the validation to show until the user tabbed to the next input.
A bit of CSS and a couple of bindings is what worked for me:
CSS:
div.validationWrapper.standard-focus.has-focus .validationMessage
{
display: none;
}
HTML:
<div class="validationWrapper standard-focus" data-bind="css: { 'has-focus': MyObservableHasFocus() }">
<input class="standard-focus" type="text" data-bind="hasFocus: MyObservableHasFocus, value: MyObservable, valueUpdate: 'afterkeydown'" />
</div>
Knockout:
self.MyObservable = ko.observable('').extend({/* Your validation here */});
self.MyObservableHasFocus = ko.observable(false);
The result is an observable that updates it's value after key up and shows the validation message after it loses focus.

Resources