How to add password matching validation in vuetify? - validation

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",
];

Related

Vue 3 checkbox component does not work in laravel as checkbox (Accept terms) situation

I'm working on a laravel 9 project that uses vue 3. For each form input has been made field components.Everything works fine except registration checkbox (Accept terms). If the checkbox is used normally in vue, it works fine. as soon as a component is made of it, it no longer works. By clicking the chackbox on and off I get the message that the accept terms is required!
I hope you can help me with this
-Vue Registration Form componetnt
<template>
<form class="text-left" method="post" #submit.prevent="submit()">
<div class="alert alert-success mb-1" v-if="success" v-html="successMessage"></div>
<div class="alert alert-danger mb-1" v-if="errors.message" v-html="errors.message"></div>
<InputFieldWithoutIcon ref="email" type="email" name="email" label="E-mail" :errors="errors" #update:field="fields.email=$event"/>
<InputFieldWithoutIcon ref="password" type="password" name="password" label="Password" :errors="errors" #update:field="fields.password=$event"/>
<InputFieldWithoutIcon ref="password_confirmation" type="password" name="password_confirmation" label="Password Confirmation" :errors="errors" #update:field="fields.password_confirmation=$event"/>
<Checkbox :text="newsletter_text" name="newsletter" type="checkbox" :errors="errors" #update:field="fields.newsletter=$event"/>
<Checkbox :text="policy_text" name="policy_accepted" type="checkbox" :errors="errors" #update:field="fields.policy_accepted=$event"/>
<SubmitButtonWithoutIcon name="create account" type="submit" custom_btn_class="btn-block btn-primary" custom_div_class=""/>
</form>
</template>
<script>
import InputFieldWithoutIcon from "../components/InputFieldWithoutIcon";
import SubmitButtonWithoutIcon from "../components/SubmitButtonWithoutIcon";
import Checkbox from "../components/Checkbox";
export default {
name: "RegistrationUser",
components: {
InputFieldWithoutIcon,
SubmitButtonWithoutIcon,
Checkbox
},
data() {
return {
fields: {
email: '',
password: '',
password_confirmation: '',
policy_accepted: '',
newsletter: '',
},
errors: {},
success: false,
successMessage: '',
newsletter_text: 'I want to receive newsletters',
policy_text: 'I accept <a class="text-bold text-underline" href="" target="_blank" title="privacy policy">the policy</a>',
}
},
methods: {
submit() {
axios.post('/api/registration', this.fields)
.then(response => {
if (response.status === 200) {
this.$refs.email.value = null;
this.$refs.password.value = null;
this.$refs.password_confirmation.value = null;
this.fields = {
email: '',
password: '',
password_confirmation: '',
policy_accepted: '',
newsletter: '',
};
this.errors = {};
this.success = true;
this.successMessage = response.data.message;
}
}).catch(errors => {
if (errors.response.status === 422 || errors.response.status === 401) {
this.errors = errors.response.data.errors;
}
});
}
}
}
</script>
-Vue Checkbox component
<template>
<div class="custom-control custom-checkbox">
<input class="custom-control-input"
:id="name"
:name="name"
:type="type"
v-model="value"
#input="updateField()"
>
<label class="custom-control-label" :for="name">
{{ text }}
</label>
<div class="invalid-feedback" v-text="errorMessage()"></div>
</div>
</template>
<script>
export default {
name: "Checkbox",
props: [
'name',
'type',
'text',
'errors'
],
data: function () {
return {
value: false
}
},
computed: {
hasError: function () {
return this.errors && this.errors[this.name] && this.errors[this.name].length > 0;
}
},
methods: {
updateField: function () {
this.clearErrors(this.name);
this.$emit('update:field', this.value)
},
errorMessage: function () {
if (this.errors && this.errors[this.name]) {
return this.errors[this.name][0];
}
},
clearErrors: function () {
if (this.hasError) {
this.errors[this.name] = null;
}
}
}
}
</script>
-Laravel Registration request
public function rules(): array
{
return [
'email' => 'required|email|unique:users',
'password' => 'required|confirmed|min:8',
'newsletter' => 'nullable|boolean',
'policy_accepted' => 'accepted'
];
}
I've found the solution.
In the checkbox template instead of using #input="updateField()" I replaced that with #change="updateField()"
That's all!

Cannot save multiple Image using Form Data

First of all this is the error that I get.
This is my first time working with Multiple Image with other Inputs.
as you can see in my FormData Headers the picture File is an Empty Array.
But inside the console it returns a file.
my question for this one that is this good? because the picture that I'm sending is a PNG one but it only says FILE.
This is my Vue Code.
<q-dialog
v-model="uploadForm"
transition-show="slide-up"
transition-hide="slide-down"
persistent
>
<q-card style="width: 700px; max-width: 50vw;">
<q-card-section>
<div class="text-h6">Add Product</div>
</q-card-section>
<div class="q-pa-md">
<form
action="http://127.0.0.1:8000/api/createProduct"
class="q-gutter-md"
method="POST"
enctype="multipart/form-data"
>
<q-input outlined label="Product name" v-model="data.productName" />
<q-input
multiple="multiple"
outlined
type="file"
accept="image/png,jpg/jpeg"
#change="onFilePicked"
ref="file"
>
<template v-slot:prepend>
<q-icon name="attach_file" />
</template>
</q-input>
<div>
<div class="image-category">
<div v-for="(image, key) in data.picture" :key="key">
<div class="image-item">
<img
class="grid"
:src="image.src"
width="300"
height="300"
/>
</div>
</div>
</div>
</div>
<q-input outlined label="Description" v-model="data.description" />
<q-input
prefix="₱ "
type="number"
outlined
label="Price"
v-model="data.price"
/>
<q-select
square
outlined
v-model="data.category_id"
:options="categories"
:option-value="'id'"
:option-label="'categoryName'"
label="Category"
/>
<div class="q-ma-md float-right">
<q-btn label="Submit" color="primary" #click="createProduct" />
<q-btn
label="Cancel"
color="primary"
flat
class="q-ml-sm"
#click="closeCreateModal"
/>
</div>
</form>
</div> </q-card
></q-dialog>
My returning Data()
data: {
picture: [],
productName: "Grizzly Bear",
description: "Machaba.",
price: "260000",
category_id: []
},
my #Change in accepting multiple Images.
onFilePicked(e) {
let selectedFiles = e.target.files;
for (let i = 0; i < selectedFiles.length; i++) {
let img = {
src: URL.createObjectURL(selectedFiles[i]),
file: selectedFiles[i]
};
this.data.picture.push(img);
console.log(this.data.picture, "inside");
}
console.log(this.data.picture, "outside");
},
My CreatingProduct method.
createProduct() {
let t = this.data;
if (
t.picture == "" ||
t.productName.trim() == "" ||
t.description.trim() == "" ||
t.price == "" ||
t.categories == ""
) {
this.uploadForm = false;
this.$q
.dialog({
title: "Incomplete Details",
message: "Please fill up all the fields in the Product",
persistent: true,
color: "negative"
})
.onOk(() => {
this.uploadForm = true;
});
} else {
let formData = new FormData();
const picture = JSON.stringify(this.data.picture); // returns [object, object] so I needed to do
//this.
const category_id = JSON.stringify(this.data.category_id);
formData.append("picture", picture);
formData.append("productName", this.data.productName);
formData.append("description", this.data.description);
formData.append("price", this.data.price);
formData.append("category_id", category_id);
let config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
this.$axios
.post("http://127.0.0.1:8000/api/createProduct", formData, config)
.then(response => {
this.products.unshift(response.data);
this.uploadForm = false;
(this.data = ""),
this.$q.notify({
icon: "info",
message: "Product Added Successfully",
color: "positive"
});
})
.catch(() => {
this.$q.notify({
color: "negative",
position: "top",
message: "Unable to save Product",
icon: "report_problem"
});
});
}
}},
my Backend in laravel
public function createProduct(Request $request)
{
$this->validate($request,[
'productName' => 'required',
'description' => 'required',
'picture' => 'required|image|mimes:jpg,png,jpeg|max:2048',
'price' => 'required',
'category_id' => 'required'
]);
$product = new Product($request->input());
if($request->hasFile('picture')){
foreach($request->file('picture') as $file){
$name = $file->getClientOriginalName();
$file->move(public_path().'/uploads/',$name);
$product->picture = $name;
}
}
$product->save();
return response()->json($product);
}
Can anyone explain to me what is wrong? I made my research and it doesn't work. and also how do you edit a multiple Image? How would you select a specific image in the Index to make that one being edit.
this is my first time working with Quasar + Laravel in creating a Product with Multiple Image.
I think you are doing some things wrong, if you use a framework try to use it as it was intended either the validations or components they have, I detail an example
new Vue({
el: '#q-app',
data: function() {
return {
uploadForm: true,
data: {
picture: [],
productName: "Grizzly Bear",
description: "Machaba.",
price: "260000",
category_id: null
},
categories: [
{ id: 1, categoryName: 'categoria 1' }
]
}
},
methods: {
createProduct() {
this.$q.loading.show()
let formData = new FormData()
formData.append("picture", this.data.picture);
formData.append("productName", this.data.productName);
formData.append("description", this.data.description);
formData.append("price", this.data.price);
formData.append("category_id", this.data.category_id.id);
axios.post('http://localhost:8000/', formData, {
'headers': {
'Content-Type': 'multipart/form-data;'
}
}).then((response) => {
alert(response)
}).catch(error => {
if (error) {
console.log('error', error)
}
}).finally(() => {
this.$q.loading.hide()
})
},
closeCreateModal() {
alert('close')
},
}
})
<link rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/#quasar/extras/material-icons/material-icons.css">
<link rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.umd.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="q-app">
<q-dialog v-model="uploadForm"
transition-show="slide-up"
transition-hide="slide-down"
persistent>
<q-card style="width: 700px; max-width: 50vw;">
<q-card-section>
<div class="text-h6">Add Product</div>
</q-card-section>
<q-card-section class="q-pt-none">
<div class="q-pa-md">
<q-form #submit="createProduct"
class="q-gutter-md">
<q-input outlined
label="Product name"
v-model="data.productName"
:rules="[val => !!val || 'Field is required']"></q-input>
<q-file multiple outlined v-model="data.picture" label="Outlined" accept="image/png,jpg/jpeg"
:rules="[val => !!val || 'Field is required']">
<template v-slot:prepend>
<q-icon name="attach_file" ></q-icon>
</template>
</q-file>
<q-input outlined
label="Description"
v-model="data.description"
:rules="[val => !!val || 'Field is required']"></q-input>
<q-input prefix="₱ "
type="number"
outlined
label="Price"
v-model="data.price"
:rules="[val => !!val || 'Field is required']"></q-input>
<q-select square
outlined
v-model="data.category_id"
:options="categories"
option-value="id"
option-label="categoryName"
label="Category"
:rules="[val => !!val || 'Field is required']"
></q-select>
<div>
<q-btn label="Submit"
color="primary"
type="submit"></q-btn>
<q-btn label="Cancel"
#click="closeCreateModal"
flat class="q-ml-sm"></q-btn>
</div>
</q-form>
</div>
</q-card-section>
</q-card>
</q-dialog>
</div>
if you see the log the "picture" attribute has an array of Object File
I leave the backend processing (laravel) at your discretion
I think it would be easier if you share a link with your problem for future reference
https://jsfiddle.net/idkc/L8xn0puq/52/

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);
}
},
})

Vuetify Form Validation - defining ES6 rules for matching inputs

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>

Resources