Problem with button activation with v-slot activator - javascript

I'm using v-slot:activator with v-btn in my Vuejs project. it works fine but the button stays hovered as if it was pressed
<v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="#F65C38" dark class="mt-1 mr-2" width="209px" v-on="on" v-bind="attrs"> Example Btn </v-btn>
</template>
<v-card>
<v-card-title>
<span class="text-h5">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-form ref="form" v-model="valid">
<v-container>
<v-row>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions class="d-flex justify-center">
<v-btn color="#f66037" plain #click="close" elevation="4" dark width="209" class="mb-6"> No </v-btn>
<v-btn color="#F65C38" #click="save" dark width="209" class="mb-6"> save </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
data:
dialog: false
watch:
dialog(val) {
val || this.close();
},
method:
close() {
this.dialog = false;
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
});
before click
after click

You can remove focus manually using native JS method:
document.activeElement.blur()
In your example you can place this row into $nextTick:
...
close() {
this.dialog = false;
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
document.activeElement.blur()
});
},
Test this at CodePen.

Related

localstorage doesn't save item without page reload first

I'm using vue.js with vuetify and saving some data in localstorage but whenever I save once and try to save a new data it calls the save function until the end but it doesn't save in localstorage being necessary to reload the page to be able to save a new data.
<v-card>
<v-card-title>
<span class="text-h5">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="12" md="12">
<v-text-field
v-model="contact.name"
label="Name"
:error-messages="nameErrors"
required
#input="$v.contact.name.$touch()"
#blur="$v.contact.name.$touch()"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="12" md="12">
<v-text-field
v-model="contact.cellPhone"
label="Phone"
v-mask="'(##) #####-####'"
:error-messages="cellPhoneErrors"
required
#input="$v.contact.cellPhone.$touch()"
#blur="$v.contact.cellPhone.$touch()"
>
</v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close"> Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save"> Save </v-btn>
</v-card-actions>
</v-card>
save() {
this.submitted = true;
this.$v.$touch();
if (this.$v.$error) {
return;
}
this.editing = false;
if (this.contact.id === 0) {
this.contact.id = this.contacts.length + 1;
this.contact.ddd = this.contact.cellPhone.substr(
1,
2
);
this.contacts.push(this.contact);
} else {
this.contacts[this.index] = this.contact;
}
localStorage.setItem(
'contacts',
JSON.stringify(this.contacts)
);
this.close();
this.contact = {
id: 0,
name: null,
cellPhone: null,
ddd: null,
};
},
I've already tried using async await but the problem remains, the only solution so far was using a normal html form instead of the vuetify form.

Login form in vue.js returns undefined

I'm trying to build a simple login/register form using Vuex to store user array. The register works, and creates a new Json object in the array. The problem is the Login because when i try to ask Vuex if a user with a specific username exists it returns me Undefined.
Vuex store code:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
users:[],
dataset:[]
},
mutations: {
addUser(state,user){
state.users.push(user);
},
addData(state,newData){
state.dataset = newData;
}
},
getters:{
getDataset: state =>{
return state.dataset;
},
getUsers: (state) =>(username) =>{
for(let i = 0; i < state.users.length(); i++){
if(state.users[i].username === username){
return i;
}
}
return -1;
}
}
,
actions: {},
modules: {},
});
Login Code:
<template>
<v-container class="my-16">
<h2 class="text-center">Per iniziare effettua il login!</h2>
<div class="text-center">
<v-dialog
v-model="dialog"
width="500"
persistent>
<template v-slot:activator="{ on, attrs }">
<v-btn
text
color="black"
v-bind="attrs"
v-on="on"
> <v-icon>mdi-login</v-icon>
Login
</v-btn>
</template>
<v-tabs v-model="tab" show-arrows background-color="primary" icons-and-text grow>
<v-tabs-slider color="green darken-4"></v-tabs-slider>
<v-tab v-for="tab in tabs" :key="tab.title">
<v-icon large>{{ tab.icon }}</v-icon>
<div class="caption py-1">{{ tab.title }}</div>
</v-tab>
<v-tab-item>
<v-card>
<v-card-text>
<v-form ref="formLogin" v-model="validLogin" >
<v-text-field label="Username" v-model="usernameLogin" prepend-icon="mdi-account-outline" :rules="nameRules"></v-text-field>
<v-text-field hint="At least 8 characters"
:type="show1 ? 'text' : 'password'"
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
label="Password" v-model="passwordLogin"
prepend-icon="mdi-lock-outline"
:rules="passwordRules"
#click:append="show1 = !show1"></v-text-field>
</v-form>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="warning"
text
#click="clearLogin"
>
Clear
</v-btn>
<v-btn
color="error"
text
#click="cancelLogin"
>
Cancel
</v-btn>
<v-btn
color="success"
text
:disabled="!validLogin"
#click="submitLogin"
>
Login
</v-btn>
</v-card-actions>
</v-card>
</v-tab-item>
<v-tab-item>
<v-card>
<v-card-text>
<v-form ref="form" v-model="validRegister" >
<v-text-field label="Name" v-model="name" prepend-icon="mdi-information-outline" :rules="nameRules"></v-text-field>
<v-text-field label="Lastname" v-model="lastname" prepend-icon="mdi-information-outline" :rules="nameRules"></v-text-field>
<v-text-field label="Username" v-model="usernameRegister" prepend-icon="mdi-account-outline" :rules="nameRules"></v-text-field>
<v-text-field hint="At least 8 characters"
:type="show1 ? 'text' : 'password'"
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
label="Password"
v-model="passwordRegister"
prepend-icon="mdi-lock-outline"
:rules="passwordRules"
#click:append="show1 = !show1"></v-text-field>
<v-menu
ref="menu"
v-model="menu"
:close-on-content-click="false"
:return-value.sync="dateOfBirth"
transition="scale-transition"
offset-y
min-width="auto"
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="dateOfBirth"
label="Date of Birth"
prepend-icon="mdi-calendar-outline"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-date-picker
v-model="dateOfBirth"
no-title
scrollable
>
<v-spacer></v-spacer>
<v-btn
text
color="error"
#click="menu = false"
>
Cancel
</v-btn>
<v-btn
text
color="success"
#click="$refs.menu.save(dateOfBirth)"
>
OK
</v-btn>
</v-date-picker>
</v-menu>
<v-textarea :counter="maxDescription" :rules="descriptionRules" label="Description" v-model="description" prepend-icon="mdi-pencil-outline"></v-textarea>
</v-form>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="warning"
text
#click="clearRegister"
>
Clear
</v-btn>
<v-btn
color="error"
text
#click="cancelRegister"
>
Cancel
</v-btn>
<v-btn
color="success"
text
:disabled="!validRegister"
#click="submitRegister"
>
Register
</v-btn>
</v-card-actions>
</v-card>
</v-tab-item>
</v-tabs>
</v-dialog>
</div>
</v-container>
</template>
<script>
export default {
data(){
return{
tab: 0,
validLogin: true,
validRegister:true,
show1: false,
dialog: false,
usernameLogin:'',
passwordLogin:'',
usernameRegister:'',
passwordRegister:'',
menu: false,
minName: 3,
minPassword: 8,
name:'',
lastname:'',
description:'',
dateOfBirth:'',
maxDescription: 150,
descriptionRules:[
v => v.length <= this.maxDescription || 'Max number of characters is 150'
],
nameRules: [
v => v.length >= this.minName || 'Minimum lenght is 3 characters'
],
passwordRules: [
v => v.length >= this.minPassword || 'Minimum lenght is 8 characters'
],
tabs:[
{title:"Login", icon:"mdi-account"},
{title:"Register", icon:"mdi-account-plus-outline"}
]
}
},
methods:{
submitRegister() {
if(this.$refs.form.validate()){
const user = {
name: this.name,
lastname: this.lastname,
username: this.usernameRegister,
password: this.passwordRegister,
description: this.description,
dateOfBirth: this.dateOfBirth
}
this.$store.commit('addUser',user);
this.cancelRegister ();
}
},
submitLogin() {
if(this.$refs.formLogin.validate()){
let user = this.usernameLogin;
console.log(user);
var index = this.$store.getters['getUsers',user];
console.log(index);
if(index != undefined){
this.$router.push('dashboard');
}
}
},
cancelRegister() {
this.$refs.form.reset();
this.validRegister = false;
this.dialog = false;
},
cancelLogin() {
this.$refs.formLogin.reset();
this.validLogin = false;
this.dialog = false;
},
clearRegister() {
this.$refs.form.reset();
this.validRegister = false;
},
clearLogin () {
this.$refs.formLogin.reset();
this.validLogin = false;
},
},
}
</script>
The method that doesn't work properly is submitLogin().
How can i solve that problem?
submitLogin() does not invoke the getUsers function correctly. The function should be called with parentheses:
var index = this.$store.getters['getUsers',user]; ❌
var index = this.$store.getters['getUsers'](user); ✅

Vuetify : Tooltip doesn't work with custom Component

My tooltip doesn't work with my Custom Component. I wrap a Dialog in a Component and I want to add a tooltip.
My Vue.js version is 2.5.17 and Vuetify 2.1.15
Componant A :
<v-tooltip bottom>
<template v-slot:activator="{ on }" v-slot:item={item}>
<ComponentB v-on="on">
</ComponentB>
</template>
<span>Hello world!</span>
</v-tooltip>
Component B:
<template>
<v-dialog v-model="dialog" persistent max-width="800px">
<template v-slot:activator="{ on }">
<v-icon small v-on="on">
add_comment
</v-icon>
</template>
<v-card>
<v-card-title>
<span class="headline">Title</span>
<v-spacer></v-spacer>
<v-btn icon #click="dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="pb-0">
Hello world!
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
text
#click="dialog = false"
>
I accept
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
I am beginner in VueJs, so may someone can help me :)
I would personally just move the tooltip into the child component, since it is technically part of the child component anyway. I did the best I could with the example code below, given the limited amount of code you provided in your question. Hopefully this helps!
<!-- Component A (Parent) -->
<template>
<ComponentB :item="item" />
</template>
<!-- Component B (Child) -->
<template>
<v-dialog v-model="dialog" persistent max-width="800px">
<v-tooltip bottom>
<template v-slot:activator="{ on }" v-slot:item="item">
<v-icon small v-on="on">
add_comment
</v-icon>
</template>
<span>Hello world!</span>
</v-tooltip>
<v-card>
<v-card-title>
<span class="headline">Title</span>
<v-spacer></v-spacer>
<v-btn icon #click="dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="pb-0">
Hello world!
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
text
#click="dialog = false"
>
I accept
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: {
item: {
type: Object,
default() {
return {};
}
}
}
};
</script>

Vuetify Dialog Component on a loop for confirmation of delete event

I've a project in which I've a datatable.vue which is looping through a data and showing some data table, have some button like edit, delete. What I want to achieve is in that loop, use a reusable dialog component, which will load and upon confirmation, trigger itemDelete method. the DialogComponent is vuetifiy (v-dialog).
<template v-slot:item.action="{ item }">
<v-btn icon color="blue" :to="{ path: updatePath, query: { id: item.id } }">
<v-icon small>mdi-pencil</v-icon>
</v-btn>
<v-btn icon color="red" #click="$emit('deleteItem', item)">
<v-icon small>mdi-delete</v-icon>
</v-btn>
</template>
this is current code of datatable, and has method deleteItem which deletes the item, I want to modify this and add reusable dialogbox (ill use this dialogbox later in other places), which will have confirmation of delete and triggers itemDelete method here.
on Vueitfy, I got this.
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
color="red lighten-2"
dark
v-bind="attrs"
v-on="on"
>
Click Me
</v-btn>
</template>
<v-card>
<v-card-title
class="headline grey lighten-2"
primary-title
>
Confirmation
</v-card-title>
<v-card-text>
Are you sure?
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
text
#click="dialog = false"
>
Confirm
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
and I made component DialogBox of it, I'm new to Vue. Thanks
There is better way to do this:
write this in your item.action slot:
<template v-slot:item.action="{ item }">
<v-btn icon color="blue" :to="{ path: updatePath, query: { id: item.id } }">
<v-icon small>mdi-pencil</v-icon>
</v-btn>
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn
color="red lighten-2"
dark
icon
v-bind="attrs"
v-on="on"
>
<v-icon small>mdi-delete</v-icon>
</v-btn>
</template>
<v-card>
<v-card-title
class="headline grey lighten-2"
primary-title
>
Confirmation
</v-card-title>
<v-card-text>
Are you sure?
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
text
#click="deleteItem(item)"
>
Confirm
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
and create the deleteItem method:
method:{
createItem(item){
this.$emit('deleteItem', item);
this.dialog = false;
}
}

Open a dialog when there is an Error Vuetify. Open dialog condiotionally

I am using vuetify. I have a dialog and i want the dialog to open only when there is an error. I am calling a back-end service from my button which saves the product in the database but when there is an duplicate product it fails. Right now even if i am clicking on the button it opens the dialog with the error message. I want the dialog to only open when there is a duplicate product. So how can i open a dialog conditionally. Success dialog when there is a success and error dialog when there is an error.
<template>
<div>
<v-card v-show="createProducts">
<v-toolbar card prominent color="primary" dark>
<v-toolbar-title>Create Product</v-toolbar-title>
</v-toolbar>
<v-card-text>
{{ product }}
<v-form ref="form" v-model="valid" lazy-validation>
<v-container>
<v-layout row wrap>
<v-flex xs6>
<v-text-field
v-model="product.name"
label="Product Name"
v-validate="'required'"
required
solo=""
data-vv-name="product name"
:error-messages="errors.collect('product name')"
></v-text-field>
</v-flex>
<v-flex xs6>
<v-text-field
v-model="product.code"
label="Product Code"
v-validate="'required'"
required
solo=""
data-vv-name="product code"
:error-messages="errors.collect('product code')"
></v-text-field>
</v-flex>
<v-flex xs12>
<v-textarea
v-model="product.description"
:auto-grow="true"
:box="false"
solo
:autofocus= "true"
:outline="false"
color="black"
background-color="white"
label="Product Description"
rows="3"
></v-textarea>
</v-flex>
<v-flex xs12>
<v-select
:items="dashboard"
label="Dashboard Template"
v-model="product.dashbaord"
data-vv-name="dashboard"
v-validate="'required'"
required
solo=""
:error-messages="errors.collect('template')"
></v-select>
</v-flex>
<!-- dialog box -->
<v-dialog v-model="dialog" width="500">
<v-btn slot="activator" color="primary" #click="createNewProduct" center>Save</v-btn>
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>Error</v-card-title>
<v-card-text>Product Code already exists</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat #click="dialog= false">No</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</v-container>
</v-form>
</v-card-text>
</v-card>
</div>
</template>
<script>
import RestResource from "../services/dataService.js";
const service = new RestResource();
export default {
name: "createProducts",
data() {
return {
createProducts: true,
valid: true,
product: {},
dialog: false,
dashboard: [
"Template1",
"Template2",
"Template3",
"Template4",
"Template5",
"Template6"
]
}
},
methods: {
async createNewProduct() {
let v = await this.$validator.validateAll();
console.log(`validation :: ${v}`)
if (v) {
let a = await service.createProduct(this.product).then(r => {
alert("Done..." + r);
}).catch (e => {
alert("Failed..." + e);
})
// this.$router.push({ name: "products"});
}
}
},
};
</script>
You can set this.dialog = true to make dialog visible
async createNewProduct() {
let v = await this.$validator.validateAll();
console.log(`validation :: ${v}`)
var self = this
if (v) {
let a = await service.createProduct(this.product).then(r => {
alert("Done..." + r);
}).catch(e => {
alert("Failed..." + e);
self.dialog = true
})
// this.$router.push({ name: "products"});
}
}
I figured it out. We just have to use v-show in our v-cards and then set it to success or failure and call the the success or failure in our method depending on our need.
<template>
<div>
<v-card v-show="createProducts">
<v-toolbar card prominent color="primary" dark>
<v-toolbar-title>Create Product</v-toolbar-title>
</v-toolbar>
<v-card-text>
{{ product }}
<v-form ref="form" v-model="valid" lazy-validation>
<v-container>
<v-layout row wrap>
<v-flex xs6>
<v-text-field
v-model="product.name"
label="Product Name"
v-validate="'required'"
required
solo=""
data-vv-name="product name"
:error-messages="errors.collect('product name')"
></v-text-field>
</v-flex>
<v-flex xs6>
<v-text-field
v-model="product.code"
label="Product Code"
v-validate="'required'"
required
solo=""
data-vv-name="product code"
:error-messages="errors.collect('product code')"
></v-text-field>
</v-flex>
<v-flex xs12>
<v-textarea
v-model="product.description"
:auto-grow="true"
:box="false"
solo
:autofocus= "false"
:outline="false"
color="black"
background-color="white"
label="Product Description"
rows="3"
></v-textarea>
</v-flex>
<v-flex xs12>
<v-select
:items="dashboard"
label="Dashboard Template"
v-model="product.dashbaord"
data-vv-name="dashboard"
v-validate="'required'"
required
solo=""
:error-messages="errors.collect('template')"
></v-select>
</v-flex>
<!-- dialog box -->
<v-dialog v-model="dialog" width="500">
<v-btn slot="activator" color="primary" #click="createNewProduct" center>Save</v-btn>
<!-- When product is successfully added -->
<v-card v-show="success">
<v-card-title class="headline grey lighten-2" primary-title>Success</v-card-title>
<v-card-text>Product Successfully Added</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat #click="showProductList">Continue</v-btn>
</v-card-actions>
</v-card>
<!-- When there is an error -->
<v-card v-show="failure">
<v-card-title class="headline grey lighten-2" primary-title>Error</v-card-title>
<v-card-text>Product Code already exists</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat #click="dialog= false">No</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</v-container>
</v-form>
</v-card-text>
</v-card>
</div>
</template>
<script>
import RestResource from "../services/dataService.js";
const service = new RestResource();
export default {
name: "createProducts",
data() {
return {
createProducts: true,
valid: true,
product: {},
dialog: false,
success: false,
failure: false,
dashboard: [
"Template1",
"Template2",
"Template3",
"Template4",
"Template5",
"Template6"
]
}
},
methods: {
async createNewProduct() {
let v = await this.$validator.validateAll();
console.log(`validation :: ${v}`)
if (v) {
let a = await service.createProduct(this.product).then(r => {
alert("Done..." + r);
// this.success = true
this.$router.push({ name: "products"});
}).catch (e => {
alert("Failed..." + e);
this.failure = true
})
}
}
},
};
</script>

Categories

Resources