Hoping I've provided enough info : Using the below links is anyone able to reproduce making Cross Field Validation form into one field that has a customer Your code must be 4 digits. & no error once 4 digit pin is entered (only any 4 numbers eg.(1234),(0000),(9999) (not any other characters))
https://codesandbox.io/s/s023f
https://vee-validate.logaretm.com/v3/advanced/rules-object-expression.html#cross-field-validation
Below is my Attempt with import { extend } from "vee-validate"; example using regex /^\d{4}$/ on equalFourDigits but I'm still seeing the custom message when 4 numbers are entered - I want to keep the custom message.
<script>
import { extend } from "vee-validate";
extend("equalFourDigits", {
params: ["equalFourDigits"],
validate: ({ equalFourDigits }) => {
if ( equalFourDigits === /^\d{4}$/ ) {
return true;
}
return false;
},
message:
"Your code must be 4 digits. {equalFourDigits}"
});
export default {
data: () => ({
firstValue: '',
secondValue: '',
equalFourDigits: ''
})
};
</script>
<template>
<ValidationObserver>
<ValidationProvider :rules="{ required: true, numeric: 4, equalFourDigits: { equalFourDigits:
equalFourDigits }}" v-slot="{ errors }">
<input type="text" v-model.number="secondValue">
<span>{{ errors[0] }}</span>
</ValidationProvider>
</ValidationObserver>
</template>
maybe you can just use :rules="{required: true, digits:4}" instead?
import {digits} from 'vee-validate/dist/rules';
extend('digits', {
...digits,
message: `only four digits ...!!!!`,
})
or
extend("equalFourDigits", {
validate: (value) => {
const regex = new RegExp(/^\d{4}$/)
if (regex.test(value)) {
return true;
}
return `Your code must be 4 digits. ${value}`;
},
});
Related
TL;DR
When using the 'required'-validator, then it doesn't update, when I insert content into a text-field.
CodeSandbox: https://codesandbox.io/s/confident-benz-x1lq8?file=/src/App.vue
Further details
I'm experiencing a wierd bug - and I've been sitting with it all day! I'm afraid that I'm missing something obvious, but I'm starting to get the feeling, that it could be a bug in the Vuelidate-library.
I have a simple form like this:
<div class="field__container">
<div>
<label for="emailToInvite">Email to invite</label> <br />
<input
type="email"
id="emailToInvite"
name="emailToInvite"
v-model="$v.emailToInvite.$model"
/>
<div class="errors" v-if="showErrors">
<p v-if="!$v.emailToInvite.required">Email to invite is required.</p>
<p v-if="!$v.emailToInvite">The written email is not valid.</p>
</div>
</div>
</div>
<button type="button" #click="submitForm()">Submit</button>
and the logic:
import { required, email } from "vuelidate/lib/validators";
export default {
data() {
return {
number: 1,
showAll: false,
submitAttempts: 0,
};
},
computed: {
showErrors() {
if (this.submitAttempts > 0) {
if (this.$v.$invalid) {
return true;
}
}
return false;
},
},
methods: {
submitForm() {
this.submitAttempts++;
},
resetAll() {
this.submitAttempts = 0;
this.$v.emailToInvite.$model = "";
},
},
validations: {
emailToInvite: {
required,
email,
},
},
}
And when I insert some text in the emailToInvite-field, then the 'required'-validator stays 'false'.
Another wierd thing is that the 'email'-validator is true, when the input is just an empty string.
All this can be seen in this CodeSandbox: https://codesandbox.io/s/confident-benz-x1lq8?file=/src/App.vue
What am I doing wrong?
I noticed a few things you should change -
First, you must add emailToInvite: '' to data:
data() {
return {
number: 1,
showAll: false,
submitAttempts: 0,
emailToInvite: ''
};
},
I'm not sure how Vuelidate works, but it seems it doesn't create the property for you. So it can't track the change properly.
Second, the showErrors computed property is set to 'false' until you press on 'Submit'. So you won't see the error message before you click on 'Submit'. Set it to 'true' if you want to see the error message (or hide it) while you type.
In my registration form I have checkbox that confirms whether the user accepted the terms and conditions. The checkbox should validate once I hit the submit button, however since the checkbox is initially unselected, the validation error shows up straight away. Eventually, the error disappears reactively once I tick the checkbox, but for this particular scenario I would like to have the validation error show up only after I hit submit (if I did not check it). I'm not getting any particular console errors, but I'm simply getting stuck on the execution. Would anyone be able to show me how I can achieve this? I'd appreciate any help!
Checkbox.vue - this is the component representing the checkbox.
<template>
<div class="check-wrapper">
<label :for="id" class="check-label">
<input v-model="checkboxValue"
:id="id"
:checked="isCheckboxChecked"
:oninput="checkCheckbox()"
type="checkbox"
name="newsletter"/>
<span v-if="labelText && !isLabelHtmlText">{{ labelText }}</span>
<span v-if="labelText && isLabelHtmlText" class="label-html" v-html="labelText"></span>
<span :class="{'check-mark-error': checkboxError}" class="check-mark"></span>
</label>
<p v-if="checkboxError" class="checkbox-error text-error">{{checkboxError}}</p>
</div>
</template>
<script>
data: () => ({
checkboxValue: false
}),
methods: {
updateValue: function () {
if (this.$props.callback) {
this.$props.callback(this.$props.id, this.$props.checkboxData, this.checkboxValue);
}
},
checkCheckbox: function () {
this.updateValue();
}
}
</script>
Register.vue - this is the parent component where the registration takes place
<template>
<BasicCheckbox :id="'terms-privacy'"
:callback="onTermsClick"
:label-text="'terms and conditions'"
:is-label-html-text="true"
:checkbox-error="termsPrivacyError"
class="terms-privacy"/>
</template>
<script>
methods: {
validateData: function (data) {
if (!this.termsPrivacyError) {
this.sendRegistration(data).then(response => {
if (response) {
console.log('Registration successful');
this.loginUser({email: data.email, password: data.password}).then(response => {
if (response) {
console.log('User logged in!');
this.$router.push({name: ROUTE_NAMES_HOME.HOME});
}
})
}
});
}
},
// Terms and Privacy Checkbox
onTermsClick: function (checkboxId, checkboxData, data) {
this.termsPrivacyError = !data ? termsPrivacyErrorText : '';
},
}
</script>
First of all, this is not an elegant solution but it works, we use a computed value to control if the error should be displayed. We update it in submit method, and cancel it when we click it checkbox for demonstration purpose.
new Vue({
el: "#app",
data: {
termsState: false,
validated: false
},
computed: {
termsError() {
return this.validated && !this.termsState
}
},
methods: {
handleTermsState() {
this.validated = false
},
handleSubmit() {
this.validated = true
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'>
<label for="terms">
Terms and Privacy Policy
<input type="checkbox" id="terms" name="terms" v-model="termsState" #change="handleTermsState">
{{ termsState }}
</label>
<p style="color: red" class="for-error terms-error" v-if="termsError">You have to agree the terms and privacy condition.</p>
<div><button type="submit" #click="handleSubmit">Submit</button></div>
</div>
From your scenario, what I understood, the validation is not happening when the user didn't check privacy and policy. If the user ticks and unticks the checkbox, the validation is working.
If that's the case, what you can do is check the child component "Checkbox.vue" data property "checkboxValue" value, as the default value is already false, and it will be true if the user did the action and tick the checkbox. Just before submitting the form, add the checkboxValue condition check.
Here is the updated Register.vue component file:
<template>
<BasicCheckbox
:id="'terms-privacy'"
:callback="onTermsClick"
:label-text="'terms and conditions'"
:is-label-html-text="true"
ref="BasicCheckbox"
:checkbox-error="termsPrivacyError"
class="terms-privacy"
/>
</template>
<script>
methods: {
validateData: function (data) {
if (!this.termsPrivacyError && this.$refs.BasicCheckbox.checkboxValue) {
this.sendRegistration(data).then(response => {
if (response) {
console.log('Registration successful');
this.loginUser({email: data.email, password: data.password}).then(response => {
if (response) {
console.log('User logged in!');
this.$router.push({name: ROUTE_NAMES_HOME.HOME});
}
})
}
});
}
},
// Terms and Privacy Checkbox
onTermsClick: function (checkboxId, checkboxData, data) {
this.termsPrivacyError = !data ? termsPrivacyErrorText : '';
},
}
</script>
What I modified only:
I added the attribute of ref for the component stage `BasicCheckbox':
ref="BasicCheckbox"
And for the validation, I just only added condition whether the ref component 'BasicCheckbox' has value `true':
if (!this.termsPrivacyError && this.$refs.BasicCheckbox.checkboxValue)
My Greeting.
To put in context, my purpose of asking this question is to be able to render a child component inside a form based on the selected option of the <app-selector> Vue component as simple and silly as that.
For the sake of simplicity. I've made a snippet down here to expose what I'm trying to figure out.
Basically, the aim is to get the component name to be rendered by using the computed property cardTypeComponent. However, I want to fathom the way cardTypeComponent is working, since I cannot see why, in one hand, the first return (return this.form) is giving the object (this.form) with the property I want (card_type) but on the other hand the second return (return this.form.card_type ? this.form.card_type + 'Compose' : '') is giving me an empty string, assuming this.form.card_type is undefined when it is clear looking at the first return that, in fact, is not taking it as undefined.
There is way more context, since once the option is selected there is a validation process from the server before setting the value inside this.form object. Moreover, the form interaction is through steps, so once the user select the option he has to click a button to reach the form fields that corresponds to that type card selected, therefore the component is not going to be rendered the very first moment the user selects an option as in the snippet approach. However, it would entangle what I'm asking. Thanks beforehand.
It is better to use the Fiddle link below.
Snippet
var appSelector = Vue.component('app-selector', {
name: 'AppSelector',
template: `<div>
<label for="card_type">Card Type:</label>
<select :name="name" value="" #change="sendSelectedValue">
<option v-for="option in options" :value="option.value">
{{ option.name }}
</option>
</select>
</div>`,
props: {
name: {
required: false,
type: String,
},
options: {
required: false,
type: Array,
}
},
methods: {
sendSelectedValue: function(ev) {
this.$emit('selected', ev.target.value, this.name)
}
}
});
var guessByImageCompose = Vue.component({
name: 'GuessByImageComponse',
template: `<p>Guess By Image Compose Form</p>`
});
var guessByQuoteCompose = Vue.component({
name: 'GuessByQuoteComponse',
template: `<p>Guess By Quote Compose Form</p>`
});
new Vue({
el: '#app',
components: {
appSelector: appSelector,
guessByImageCompose: guessByImageCompose,
guessByQuoteCompose: guessByQuoteCompose,
},
data() {
return {
form: {},
card_types: [
{
name: 'Guess By Quote',
value: 'GuessByQuote'
},
{
name: 'Guess By Image',
value: 'GuessByImage'
}
],
}
},
computed: {
cardTypeComponent: function() {
return this.form; // return { card_type: "GuessByImage" || "GuessByQuote" }
return this.form.card_type ? this.form.card_type + 'Compose' : ''; // return empty string ("") Why?
}
},
methods: {
setCardType: function(selectedValue, field) {
this.form[field] = selectedValue;
console.log(this.form.card_type); // GuessByImage || GuessByQuote
console.log(this.cardTypeComponent); // empty string ("") Why?
}
},
mounted() {
console.log(this.cardTypeComponent); // empty string ("")
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form action="#" method="post">
<app-selector
:name="'card_type'"
:options="card_types"
#selected="setCardType"
>
</app-selector>
{{ cardTypeComponent }} <!-- Always empty string !-->
<component v-if="cardTypeComponent !== ''" :is="cardTypeComponent">
</component>
</form>
</div>
https://jsfiddle.net/k7gnouty/2/
You're setting a property on this.form which is not initialized first in data. This means you have run into Vue's change detection caveat. Use Vue.set when setting it:
methods: {
setCardType: function(selectedValue, field) {
Vue.set(this.form, field, selectedValue);
}
}
Alternatively, you could declare the properties first if that works better for you.
I've written a custom validator like this:
created () {
this.$validator.extend('validateCert', {
getMessage: (field) => {
return field + ' is not a valid certificate';
},
validate: (value) => {
return value.startsWith('-----BEGIN CERTIFICATE-----') && value.endsWith('-----END CERTIFICATE-----');
}
});
}
I've attached it to a text area inside a modal:
<div class="pb-3 mr-4">
<b-form-textarea
type="text"
v-validate="'required|validateCert'"
data-vv-validate-on="change"
v-model.trim="signedCerts[index]"
data-vv-scope="uploadCert"
:name="'certificate_' + index"
:class="[{'is-invalid': errors.has('certificate_' + index)}]"
rows="15"/>
<fr-validation-error :validatorErrors="errors" :fieldName="'certificate_' + index"></fr-validation-error>
</div>
Then - on button click I do the following:
uploadCerts (event) {
let idmInstance = this.getRequestService(),
body = {
fromCSR: true,
certs: _.each(this.signedCerts, (cert) => {
JSON.stringify(cert);
})
};
this.$validator.validateAll('uploadCert').then((valid) => {
// Prevent modal from closing
event.preventDefault();
if (valid) { // some more logic
If I inspect the computed errors object, I will see my failed validation:
{
"items": [
{
"id": "19",
"field": "certificate_0",
"msg": "certificate_0 is not a valid certificate",
"rule": "validateCert",
"scope": "uploadCert",
"regenerate": {
"_custom": {
"type": "function",
"display": "<span>ƒ</span> regenerate()"
}
}
}
]
}
and the value of 'valid' (either true or false) is accurate at all times. I'm just not seeing my error classes being triggered.
Hard to completely answer the question because part of it depends on what happens in fr-validation-error, but I think the problem is how you're using scopes.
When you define data-vv-scope="uploadCert" that means that every reference to field-name has to be prefaced by uploadCert. in errors. So when you specify in your :class that errors.has('certificates_'+index), you have to change that to errors.has('uploadCert.certificates_'+index).
Here's how it would look in full (minus the bootstrap-vue and multiple fields bits):
<textarea
v-validate="'required|validateCert'"
data-vv-validate-on="change"
data-vv-scope="uploadCert"
v-model.trim="signedCert"
name="certificate"
class="form-control"
:class="{ 'is-invalid': errors.has('uploadCert.certificate') }"
rows="10"
>
</textarea>
Full working example for one certificate upload field: https://codesandbox.io/s/z2owy0r2z3
How do you create a simple, custom rule using the jQuery Validate plugin (using addMethod) that doesn't use a regex?
For example, what function would create a rule that validates only if at least one of a group of checkboxes is checked?
You can create a simple rule by doing something like this:
jQuery.validator.addMethod("greaterThanZero", function(value, element) {
return this.optional(element) || (parseFloat(value) > 0);
}, "* Amount must be greater than zero");
And then applying this like so:
$('validatorElement').validate({
rules : {
amount : { greaterThanZero : true }
}
});
Just change the contents of the 'addMethod' to validate your checkboxes.
$(document).ready(function(){
var response;
$.validator.addMethod(
"uniqueUserName",
function(value, element) {
$.ajax({
type: "POST",
url: "http://"+location.host+"/checkUser.php",
data: "checkUsername="+value,
dataType:"html",
success: function(msg)
{
//If username exists, set response to true
response = ( msg == 'true' ) ? true : false;
}
});
return response;
},
"Username is Already Taken"
);
$("#regFormPart1").validate({
username: {
required: true,
minlength: 8,
uniqueUserName: true
},
messages: {
username: {
required: "Username is required",
minlength: "Username must be at least 8 characters",
uniqueUserName: "This Username is taken already"
}
}
});
});
// add a method. calls one built-in method, too.
jQuery.validator.addMethod("optdate", function(value, element) {
return jQuery.validator.methods['date'].call(
this,value,element
)||value==("0000/00/00");
}, "Please enter a valid date."
);
// connect it to a css class
jQuery.validator.addClassRules({
optdate : { optdate : true }
});
Custom Rule and data attribute
You are able to create a custom rule and attach it to an element using the data attribute using the syntax data-rule-rulename="true";
So to check if at least one of a group of checkboxes is checked:
data-rule-oneormorechecked
<input type="checkbox" name="colours[]" value="red" data-rule-oneormorechecked="true" />
addMethod
$.validator.addMethod("oneormorechecked", function(value, element) {
return $('input[name="' + element.name + '"]:checked').length > 0;
}, "Atleast 1 must be selected");
And you can also override the message of a rule (ie: Atleast 1 must be selected) by using the syntax data-msg-rulename="my new message".
NOTE
If you use the data-rule-rulename method then you will need to make sure the rule name is all lowercase. This is because the jQuery validation function dataRules applies .toLowerCase() to compare and the HTML5 spec does not allow uppercase.
Working Example
$.validator.addMethod("oneormorechecked", function(value, element) {
return $('input[name="' + element.name + '"]:checked').length > 0;
}, "Atleast 1 must be selected");
$('.validate').validate();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.14.0/jquery.validate.min.js"></script>
<form class="validate">
red<input type="checkbox" name="colours[]" value="red" data-rule-oneormorechecked="true" data-msg-oneormorechecked="Check one or more!" /><br/>
blue<input type="checkbox" name="colours[]" value="blue" /><br/>
green<input type="checkbox" name="colours[]" value="green" /><br/>
<input type="submit" value="submit"/>
</form>
Thanks, it worked!
Here's the final code:
$.validator.addMethod("greaterThanZero", function(value, element) {
var the_list_array = $("#some_form .super_item:checked");
return the_list_array.length > 0;
}, "* Please check at least one check box");
You can add a custom rule like this:
$.validator.addMethod(
'booleanRequired',
function (value, element, requiredValue) {
return value === requiredValue;
},
'Please check your input.'
);
And add it as a rule like this:
PhoneToggle: {
booleanRequired: 'on'
}
For this case: user signup form, user must choose a username that is not taken.
This means we have to create a customized validation rule, which will send async http request with remote server.
create a input element in your html:
<input name="user_name" type="text" >
declare your form validation rules:
$("form").validate({
rules: {
'user_name': {
// here jquery validate will start a GET request, to
// /interface/users/is_username_valid?user_name=<input_value>
// the response should be "raw text", with content "true" or "false" only
remote: '/interface/users/is_username_valid'
},
},
the remote code should be like:
class Interface::UsersController < ActionController::Base
def is_username_valid
render :text => !User.exists?(:user_name => params[:user_name])
end
end
Step 1 Included the cdn like
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
Step 2 Code Like
$(document).ready(function(){
$("#submit").click(function () {
$('#myform').validate({ // initialize the plugin
rules: {
id: {
required: true,
email: true
},
password: {
required: true,
minlength: 1
}
},
messages: {
id: {
required: "Enter Email Id"
},
password: {
required: "Enter Email Password"
}
},
submitHandler: function (form) { // for demo
alert('valid form submitted'); // for demo
return false; // for demo
}
});
}):
});