PolyVal provides field comparison rules that allow you to validate a field’s value against another field in the same form. This is useful for password confirmation, ensuring unique values, and implementing complex validation logic.
Equals validation
The equals rule validates that a field’s value matches another field’s value. This is commonly used for password confirmation fields.
Password confirmation example
Here’s a real example from the PolyVal source code:
import { validate , SimpleValidationSchema } from 'polyval' ;
const schema : SimpleValidationSchema = {
password: {
type: 'string' ,
required: true ,
min: 8
},
confirmPassword: {
type: 'string' ,
required: true ,
equals: 'password' // Must match the password field
}
};
const data = {
password: 'Secure1@Password' ,
confirmPassword: 'Secure1@Password'
};
const errors = validate ( schema , data , { lang: 'en' });
// No errors - passwords match
How equals works
The equals rule compares the field’s value with the value of the specified field:
const schema = {
email: {
type: 'string' ,
required: true ,
email: true
},
confirmEmail: {
type: 'string' ,
required: true ,
equals: 'email' // Must match the email field
}
};
The comparison is strict and case-sensitive. The values must match exactly.
Equals with boolean values
For boolean fields, equals can validate that the value is specifically true or false:
const schema = {
acceptTerms: {
type: 'boolean' ,
required: true ,
equals: true // Must be checked (true)
}
};
const validData = {
acceptTerms: true
};
const invalidData = {
acceptTerms: false
};
const errors = validate ( schema , invalidData , { lang: 'en' });
// AcceptTerms: Must be checked
Use equals: true for terms and conditions checkboxes that must be accepted.
NotEquals validation
The notEquals rule validates that a field’s value is different from another field’s value. This is useful when you need to ensure fields have distinct values.
Password change example
Ensure the new password is different from the old password:
const schema = {
oldPassword: {
type: 'string' ,
required: true
},
password: {
type: 'string' ,
required: true ,
min: 8 ,
notEquals: 'oldPassword' // Must not match oldPassword
}
};
const invalidData = {
oldPassword: 'OldPass123!' ,
password: 'OldPass123!' // Same as old password
};
const errors = validate ( schema , invalidData , { lang: 'en' });
// Password: Must not match the oldPassword field
Multiple field comparison
You can use field comparison rules on multiple fields:
const schema = {
username: {
type: 'string' ,
required: true ,
min: 3 ,
max: 20
},
displayName: {
type: 'string' ,
required: true ,
notEquals: 'username' // Display name must differ from username
}
};
Validation order
Field comparison validation occurs after basic type and format validation:
Valid example
Invalid example
const schema = {
password: {
type: 'string' ,
required: true ,
min: 8
},
confirmPassword: {
type: 'string' ,
required: true ,
min: 8 ,
equals: 'password'
}
};
const data = {
password: 'Secure1@Password' ,
confirmPassword: 'Secure1@Password'
};
const errors = validate ( schema , data , { lang: 'en' });
// [] - No errors
Error messages
Default error messages for field comparison are available in both English and Turkish:
equals : ( field : string ) => `Must match the ${ field } field`
notEquals : ( field : string ) => `Must not match the ${ field } field`
equals : ( field : string ) => ` ${ field } alanı ile eşleşmelidir`
notEquals : ( field : string ) => ` ${ field } alanı ile eşleşmemelidir`
Customizing comparison messages
You can customize field comparison error messages:
Global customization
Field-specific customization
const errors = validate ( schema , data , {
lang: 'en' ,
customMessages: {
equals : ( field ) => `This field must match ${ field } ` ,
notEquals : ( field ) => `This field must be different from ${ field } `
}
});
Field-specific messages have higher priority than global comparison messages. See Customizing messages for the complete priority order.
Complete registration example
Here’s a complete user registration example from the PolyVal source code:
import { validate , SimpleValidationSchema } from 'polyval' ;
const userRegistrationSchema : SimpleValidationSchema = {
username: {
type: 'string' ,
required: true ,
min: 3 ,
max: 20 ,
regex: '^[a-zA-Z0-9_]+$'
},
email: {
type: 'string' ,
required: true ,
email: true
},
password: {
type: 'string' ,
required: true ,
min: 8 ,
regex: '^(?=.*[a-z])(?=.*[A-Z])(?=.* \\ d)(?=.*[@$!%*?&])[A-Za-z \\ d@$!%*?&]+$'
},
confirmPassword: {
type: 'string' ,
required: true ,
equals: 'password' // Must match password
},
age: {
type: 'number' ,
min: 18
},
acceptTerms: {
type: 'boolean' ,
required: true ,
equals: true // Must be checked
}
};
const validData = {
username: 'johndoe' ,
email: '[email protected] ' ,
password: 'Secure1@Password' ,
confirmPassword: 'Secure1@Password' ,
age: 25 ,
acceptTerms: true
};
const errors = validate ( userRegistrationSchema , validData , {
lang: 'en' ,
customMessages: {
fields: {
confirmPassword: {
equals: 'Passwords do not match'
},
acceptTerms: {
equals: 'You must accept the terms and conditions'
}
}
}
});
if ( errors . length === 0 ) {
console . log ( 'Registration successful!' );
}
Best practices
Field comparison best practices
Always validate both fields : Even though confirmPassword uses equals, still mark it as required to ensure it’s not empty.
Use field-specific messages : Customize error messages for better user experience, especially for password confirmation.
Order matters : The referenced field (e.g., password) should appear before the comparing field (e.g., confirmPassword) in your schema for logical flow.
Type consistency : Both fields being compared should have the same type. The library validates types before comparison.
Case sensitivity : Comparisons are case-sensitive. Use custom validators if you need case-insensitive comparison.
Field comparison only validates against fields in the same data object. You cannot compare across nested objects or arrays.