Vuetify Form Validation - defining ES6 rules for matching inputs - validation

I have a (Vuetify) form with an email input that uses ES6 & regex to check if it's a valid email. How would I set up another emailConfirmationRules ruleset to check if the emailConfirmation input matches the email input?
<template>
<v-form v-model="valid">
<v-text-field label="Email Address"
v-model="email"
:rules="emailRules"
required></v-text-field>
<v-text-field label="Confirm Email Address"
v-model="emailConfirmation"
:rules="emailConfirmationRules"
required></v-text-field>
</v-form>
<template>
export default {
data () {
return {
valid: false,
email: '',
emailConfirmation: '',
emailRules: [
(v) => !!v || 'E-mail is required',
(v) => /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mail must be valid'
],
emailConfirmationRules: [
(v) => !!v || 'Confirmation E-mail is required',
] (v) => ??? || 'Confirmation E-mail does not match'
}
}

Rules are not the proper way of handling field confirmation.
emailConfirmationRules are triggered only when emailConfirmation changes, but if you change email again, your fields won't match anymore without any break of rules.
You have to handle this manually:
methods: {
emailMatchError () {
return (this.email === this.emailConfirmation) ? '' : 'Email must match'
}
}
<v-text-field v-model='emailConfirmation' :error-messages='emailMatchError()'></v-text-field>
There may be an alternative way of doing this with vee-validate too.

You can accomplish the same with a computed rule.
computed: {
emailConfirmationRules() {
return [
() => (this.email === this.emailToMatch) || 'E-mail must match',
v => !!v || 'Confirmation E-mail is required'
];
},
}

emailConfirmationRules: [
(v) => !!v || 'Confirmation E-mail is required',
(v) => v == this.email || 'E-mail must match'
],

<template>
<v-text-field
v-model="employee.email"
:rules="emailRules"
required
validate-on-blur
/>
</template>
<script>
data() {
return {
emailRules: [
v => !!v || "E-mail is required",
v =>
/^\w+([.-]?\w+)*#\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) ||
"E-mail must be valid"
],
}
</script>

Check this post
https://stackoverflow.com/a/58883995/9169379
<template>
<v-input v-model="firstPassword" />
<v-input :rules=[rules.equals(firstPassword)] v-model="secondPassword" />
</template>
<script>
data() {
firstPassword: '',
secondPassword: '',
rules: {
equals(otherFieldValue) {
return v => v === otherFieldValue || 'Not equals';
}
}
}
</script>

Related

Looking for a way to translate(i18n) laravels form validation rules inside a Vue form

So I have build an form in laravel with Vue with some validation rules and this works, when there's an error it will show me an message but I also have an locales switcher present which also works for the text in the form but not for the validation output, so there are always in English. I am using the i18n
plugin to translate the text.
Is there a way to make the validation rules i18n ready?
registerController.php
protected function validator(array $data)
{
$customMessages = [
'name' => 'some custom message can go here...',
];
$validator = Validator::make($data, [
'name' => ['required', 'min:2', 'string', 'max:100'],
'email' => ['required', 'email', 'string', 'max:255', 'unique:users'],
'password' => [
'required'
'min:10',
'regex:/[a-z]/', // must contain at least one lowercase letter
'regex:/[A-Z]/', // must contain at least one uppercase letter
'regex:/[0-9]/', // must contain at least one digit
'regex:/[#$!%*#?&]/', // must contain a special character
],
], $customMessages);
return $validator;
}
componentForm.vue
<template>
<form #submit.prevent="formSubmit">
<el-input type="text" name="name" v-model="fields.name"
label="Name" :error="errors.name"/>
<el-input type="email" name="email" v-model="fields.email"
label="E-mailaddress" :error="errors.email"/>
<el-input type="password" name="password" v-model="fields.password"
label="Password" :error="errors.password"/>
<button type="submit" class="btn btn--primary btn--xl btn--block">
{{ $t("auth.register") }}
</button>
</form>
</template>
<script>
import espressoLocale from '../../Components/Contextual/Locale';
import espressoCard from '../../Components/UI/Card';
import elInput from '../../Components/Forms/Input';
export default {
components: {
elInput,
},
data() {
return {
fields:{
name: "",
email: "",
password: "",
},
errors: {
name: null,
email: null,
password: null,
},
}
},
methods: {
resetFields () {
this.fields.name = "";
this.fields.email = "";
this.fields.password = "";
},
formSubmit (e) {
e.preventDefault();
this.errors = {};
axios.get('/sanctum/csrf-cookie').then(response => {
axios({
method: 'post',
url: '/api/register',
data: {
name: this.fields.firstname,
email: this.fields.email,
password: this.fields.password,
},
validateStatus: (status) => {
return true;
}
}).then(response => {
if (response.data.success) {
} else {
this.errors = response.data.errors || {};
}
}).catch(function (error) { });
});
}
},
}
</script>
You should write :error="$t(errors.name)" in your components as you write {{ $t("auth.register") }} to show translated text about register. I assume that you get i18n locale structured object in your errors response, something like this:
errors: {
name: 'errors.name',
...
}

Vue search customer detail based on phone number input and auto fill address

I try to take customer (name, address) based on phone number input. if customer is there then I want the name and address to auto-filled based on customer data we got. below are my code.
my form
<v-row>
<v-col cols="12">
<v-text-field :rules="contactRules" v-model="form.contact" label="Contact" type="number" #input="searchCustomerContact"></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field :rules="nameRules" v-model="form.name" label="Name"></v-text-field>
</v-col>
<v-col cols="12">
<v-textarea rows="1" :rules="address1Rules" v-model="form.address_line_1" label="Address" auto-grow clearable clear-icon="cancel"></v-textarea>
</v-col>
</v-row>
data()
data() {
return {
valid: false,
form: {},
nameRules: [v => !!v || "Name is required"],
contactRules: [v => !!v || "Contact is required"],
address1Rules: [v => !!v || "Address is required"]
};
},
search Customer ()
searchCustomerContact(val) {
if (val.length == 10) {
this.$axios
.get("customers?contact=" + val)
.then(res => {
if (res.data != null) {
this.form.name = res.data.name;
this.form.address_line_1 = res.data.address_line_1;
} else {
this.form.name = null;
this.form.address_line_1 = null;
}
})
.catch(err => {
throw err;
});
}
}
My controller
public function getCustomerByContact()
{
$data = Order::where('contact', request()->contact)->first();
return response()->json($data, 200);
}
Route
Route::get('customers', 'OrderController#getCustomerByContact');
My problem is when data is found, it cannot auto-filled the name and address field. I'm new in vue. If contact is not there then the (name, contact) field is always empty so I think no need to do that part (maybe)
thanks in advance
For those who have this kind of problem, I solved by editing like below
<v-text-field :rules="nameRules" v-model="form.name" label="Name" :value="form.name"></v-text-field>
<v-textarea rows="1" :rules="address1Rules" v-model="form.address_line_1" label="Address" auto-grow clearable clear-icon="cancel" :value="form.address_line_1"></v-textarea>
data() {
return {
form: {
name: "",
address_line_1: ""
},
};
},
add :value in form text-field and name & address in data()->form

How to extract a <form> element from Vuetify's <v-form> component

I have a Vuetify form with a ref, like this
<v-form ref="form" method="post" #submit.prevent="handleSubmit">
Onto it I attached a default-preventing submit handler whose method is as follows:
window.fetch(`${this.$store.state.apiServer}/some-url`, {
method: 'post',
body: new FormData(this.$refs.form)
}).then(response => {
// Do stuff with the response
}).catch(() => {
// HCF
});
The problem is new FormData() only accepts a pure HTML <form> element, while this.$refs.form is a VueComponent. Is there a way I can grab the <form> from inside a <v-form>?
You can get the form element using this.$refs.form.$el
Here is the working codepen: https://codepen.io/chansv/pen/OJJOXPd?editors=1010
<div id="app">
<v-app id="inspire">
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-text-field
v-model="name"
:counter="10"
:rules="nameRules"
label="Name"
required
></v-text-field>
<v-text-field
v-model="email"
:rules="emailRules"
label="E-mail"
required
></v-text-field>
<v-btn type="submit" #click="formSubmit">submit</v-btn>
</v-form>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
valid: true,
name: '',
nameRules: [
v => !!v || 'Name is required',
v => (v && v.length <= 10) || 'Name must be less than 10 characters',
],
email: '',
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+\..+/.test(v) || 'E-mail must be valid',
],
}),
methods: {
formSubmit() {
console.log(this.$refs.form.$el);
}
},
})

How to validate Dynamic input in vuejs / Laravel

I'm catching errors via interceptors and a Toast for UI. Usually the error is caught by the interceptor and displayed via the toast for one-time inputs, i'm trying to catch errors for an uncertain number of inputs. So far looping through the input array and setting rules does not work.
Component.vue
<template>
<div>
<div class="form-group" v-for="(input,k) in inputs" :key="k">
<input type="text" id="name" placeholder="Name" v-model="input.name" />
<span>
<i class="fas fa-minus" #click="remove(k)" v-show="k || ( !k && inputs.length > 1)"></i>
<i class="fas fa-plus" #click="add(k)" v-show="k == inputs.length-1"></i>
</span>
</div>
<button #click="addName">Create</button>
</div>
</template>
<script>
export default {
data() {
return {
inputs: [{name: ''}]
}
},
methods: {
add(index) {
this.inputs.push({ name: ''});
console.log( this.inputs);
},
remove(index) {
this.inputs.splice(index, 1);
},
addName() {
axios.post('/user', {userinputs:this.inputs}).then(response => {})
.catch(error => {
console.log(error);
});
}
}
}
</script>
Controller:
public function store(Request $request)
{
$rules = [
'userinputs' => 'required|max:255',
];
foreach ($request->input('userinputs') as $key => $my_object_in_array) {
$rules[$my_object_in_array['name']] = 'required|max:10';
}
return $rules;
}
I hope this is what you wanted to achieve.
public function store(Request $request)
{
$rules = [
'userinputs.*.name' => 'required|max:255',
];
$validator = \Illuminate\Support\Facades\Validator::make($request->all(), $rules);
if ($validator->fails()) {
//Return the errors as JSON
return response()->json(['success' => 'false', 'errors' => $validator->errors()], 400);
}
//Do something
return ['success' => 'true'];
}

How to add password matching validation in vuetify?

I am trying to create a profile form which have two fields
password and rePassword. basically, Both of them should be the same.
I tried using different codes found on the web and different approaches. Some of them worked but. It did'nt actually fit in with the code.
Here is the piece of code:
Profile.vue:
<v-layout>
<v-flex xs12 sm6>
<v-text-field
v-model="password"
:append-icon="show ? 'visibility' : 'visibility_off'"
:rules="[rules.required, rules.min]"
:type="show ? 'text' : 'password'"
name="password"
label="Enter Password"
hint="At least 8 characters"
counter
#click:append="show = !show"
></v-text-field>
</v-flex>
<v-flex xs12 sm6>
<v-text-field
v-model="rePassword"
:append-icon="show1 ? 'visibility' : 'visibility_off'"
:rules="[rules.required, rules.min]"
:type="show1 ? 'text' : 'password'"
name="input-10-1"
label="Re-enter Password"
hint="At least 8 characters"
counter
#click:append="show1 = !show1"
></v-text-field>
</v-flex>
</v-layout>
This is how the script looks like:
Profile.vue (script):
data() {
return {
show: false,
show1: false,
password: 'Password',
rePassword: 'Password',
rules: {
required: value => !!value || 'Required.',
min: v => v.length >= 8 || 'Min 8 characters',
emailMatch: () => ('The email and password you entered don\'t match')
},
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+/.test(v) || 'E-mail must be valid'
],
date: new Date().toISOString().substr(0, 10),
menu: false,
items: ['male', 'female'],
address: '',
title: "Image Upload",
dialog: false,
imageName: '',
imageUrl: '',
imageFile: ''
}
},
methods: {
pickFile() {
this.$refs.image.click()
},
onFilePicked(e) {
const files = e.target.files
if(files[0] !== undefined) {
this.imageName = files[0].name
if(this.imageName.lastIndexOf('.') <= 0) {
return
}
const fr = new FileReader ()
fr.readAsDataURL(files[0])
fr.addEventListener('load', () => {
this.imageUrl = fr.result
this.imageFile = files[0] // this is an image file that can be sent to server...
})
} else {
this.imageName = ''
this.imageFile = ''
this.imageUrl = ''
}
},
}
,
validate() {
if (this.$refs.form.validate()) {
this.snackbar = true
}
},
reset() {
this.$refs.form.reset()
}
How do I add a password matching feature in the validation using vuetify.
Thanks
You can define custom rules:
computed: {
passwordConfirmationRule() {
return () => (this.password === this.rePassword) || 'Password must match'
}
}
and use it
<v-flex xs12 sm6>
<v-text-field
v-model="rePassword"
:append-icon="show1 ? 'visibility' : 'visibility_off'"
:rules="[rules.required, rules.min, passwordConfirmationRule]"
:type="show1 ? 'text' : 'password'"
name="input-10-1"
label="Re-enter Password"
hint="At least 8 characters"
counter
#click:append="show1 = !show1"
/>
</v-flex>
very easiest way is
using v-model (password and confirm_password), no need to use computation
Rule
:rules="[v => !!v || 'field is required']"
Or
:rules="[(password!="") || 'field is required']"
in password
<v-text-field label="Password*" v-model="password" type="password" required :rules="[v => !!v || 'field is required']"></v-text-field>
confirm password field
Rule
:rules="[(password === confirm_password) || 'Password must match']"
code:
<v-text-field label="Confirm Password*" v-model="confirm_password" type="password" required :rules="[(password === confirm_password) || 'Password must match']"></v-text-field>
VeeValidate is great for form validation but in my opinion is overkill for resolving this question when it can be achieved in Vuetify alone.
Following on from #ittus answer, you need to remove the arrow function in passwordConfirmationRule to access this:
return this.password === this.rePassword || "Password must match";
See this codesandbox working example (also now using Vuetify 2.x)
Very simply using Vee-validate:
<div id="app">
<v-app id="inspire">
<form>
<v-text-field
ref="password"
type="password"
v-model="pass"
v-validate="'required'"
:error-messages="errors.collect('pass')"
label="Pass"
data-vv-name="pass"
required
></v-text-field>
<v-text-field
v-model="pass2"
type="password"
v-validate="'required|confirmed:password'"
:error-messages="errors.collect('pass2')"
label="Pass 2"
data-vv-name="pass"
required
></v-text-field>
<v-btn #click="submit">submit</v-btn>
<v-btn #click="clear">clear</v-btn>
</form>
</v-app>
</div>
Vue.use(VeeValidate)
new Vue({
el: '#app',
$_veeValidate: {
validator: 'new'
},
data: () => ({
pass: '',
pass2: "",
}),
methods: {
submit () {
this.$validator.validateAll()
.then(result => {
console.log(result)
})
},
clear () {
this.pass = ''
this.pass2 = ''
}
}
})
Remember to install vee-validate first and restart your local-server.
link to codepen
link to docs
This works right for me!
// template
<v-text-field v-model="password" label="password"></v-text-field>
<v-text-field v-model="confirmPassword" :rules="confirmPasswordRules" label="confirmPassword"></v-text-field>
// script
computed: {
confirmPasswordRules() {
const rules = [(this.password === this.confirmPassword) || "Password must match."];
return rules;
},
}
This is not a clean solution definitely, but works fine.
<v-text-field
type="password"
v-model="password"
:rules="passwordRules"
required
></v-text-field>
<v-text-field
type="password"
v-model="passwordConfirm"
:rules="passwordConfirmRules"
required
></v-text-field>
let globalPassword = "";
watch: {
password() {
globalPassword = this.password;
},
},
passwordConfirmRules = [
(v) => v === globalPassword || "Password must match",
];

Resources