v-btn of v-dialog disappears when adding v-bind to attribute - vue.js

With Vuetify, I created a dialog in which I want to bind placeholders of my different v-text-field.
<template>
<v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="600px">
<template v-slot:activator="{ on }">
<v-btn text icon dark v-on="on" color="black">
<v-icon>mdi-pencil-outline</v-icon>
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">Edit profile</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="f_name" :label="getName()" required></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
export default {
name: "edit_form_personal",
data() {
return {
dialog: false,
f_name: '',
l_name: '',
email: '',
phone: ''
}
},
methods: {
getName() {
return 'joe'
}
},
}
</script>
I am using getName() to bind 'joe' inside my form.
The issue is that when I change
<v-text-field v-model="f_name" label="First Name" required></v-text-field>
to :
<v-text-field v-model="f_name" :label="getName()" required></v-text-field>
the <v-btn> disappears as if the v-dialog was broken.

Move the getName function from methods to computed property
see the working codepen here: https://codepen.io/chansv/pen/xxxOJGq?editors=1010
<script>
export default {
name: "edit_form_personal",
data() {
return {
dialog: false,
f_name: '',
l_name: '',
email: '',
phone: ''
}
},
computed: {
getName() {
return 'joe'
}
},
}
</script>
also use getName instead of getName() in text field and :label instead of :v-label
<v-text-field v-model="f_name" :label="getName" required></v-text-field>

Related

Vue.js & Django - axios post response status 200 but called catch error

i'm learning Vue.js and Django.
I want to post user info.
axios response status 200 code, but catch function is called.
If the response is successful, the login 'success dialog' is called , but the login 'fail dialog' is also called.
this image is console log.
enter image description here
please help me!
here is my code.
<script>
// LoginDialog.vue
import axios from "axios";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
export default {
name: "LoginDialog",
components: {},
props: {
dialog: Boolean,
},
data() {
return {
showPassword: false,
dialogClose: {
success: false,
fail: false,
},
me: {},
};
},
setup() {},
created() {},
mounted() {},
unmounted() {},
computed: {
dialogOpen: {
get() {
return this.dialog;
},
set(val) {
this.$emit("close", val);
},
},
},
methods: {
save() {
console.log("save()...");
const postData = new FormData(document.getElementById("login-from"));
axios
.post("/api/login/", postData)
.then((res) => {
console.log("LOGIN POST RES", res);
// alert(`user ${res.data.username} login OK`);
this.dialogClose.success = true;
this.this.me = res.data;
})
.catch((err) => {
console.log("LOGIN POST ERR.RESPONSE", err.response);
this.dialogClose.fail = true;
// alert("login FAIL");
});
},
},
};
</script>
// LoginDialog.vue
<template>
<div>
<!-- 로그인 dialog -->
<v-dialog v-model="dialogOpen" max-width="500">
<v-card width="800" class="mx-auto mt-5">
<v-toolbar color="primary" dark flat>
<v-toolbar-title class="pb-0">
<h2>Login</h2>
</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-form id="login-from" ref="loginForm">
<v-text-field
label="Username"
name="username"
prepend-icon="mdi-account-circle"
></v-text-field>
<v-text-field
:type="showPassword ? 'text' : 'password'"
label="Password"
name="password"
prepend-icon="mdi-lock"
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
#click:append="showPassword = !showPassword"
autocomplete="on"
></v-text-field>
</v-form>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text
color="grey"
center
#click="(dialogOpen = false), $refs.loginForm.reset()"
>Cancel</v-btn
>
<v-btn color="success" center #click="dialogOpen = false"
>Register</v-btn
>
<v-btn color="info" center #click="save()">Login</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- login success 팝업창 -->
<v-dialog v-model="dialogClose.success" max-width="290">
<v-card>
<v-card-title class="text-h5"> Login Success! </v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
text
#click="dialogClose.success = false"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- login fail 팝업창 -->
<v-dialog v-model="dialogClose.fail" max-width="290">
<v-card>
<v-card-title class="text-h5"> Login Fail! </v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click="dialogClose.fail = false">
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
So the Axios call goes well, but after that, in the .then block an error is thrown and caught by the .catch block. This is also why the err.response is empty, because that is an error object of Axios. If you log the err instead of err.response you might see the actual error (as Akzhol Kadylbek suggested in his comment).
Looking at your code, I presume that error comes from this line:
this.this.me = res.data;
/// change to this.me = res.data;
The double this. looks like a typo.
Hope this helps.

Vuetify reset form after submitting

I am using a form inside dialog using vuetify.
Imported the component in page like this -
<template>
<div>
<topicForm :dataRow="dataRow" v-model="dialog" />
</div>
</template>
methods: {
openDialog(item = {}) {
this.dataRow = item;
this.dialog = true;
},
}
Dialog form code -->
<template>
<div>
<v-dialog v-model="value" max-width="500px" #click:outside="close">
<v-card outlined class="pt-5">
<v-form ref="form" class="px-3">
<v-card-text class="pt-5">
<v-row no-gutters>
<v-text-field
required
outlined
label=" Name"
v-model="data.name"
:rules="[rules.required]"
></v-text-field>
</v-row>
<v-row no-gutters>
<v-textarea
required
outlined
label=" Description"
v-model="data.description"
></v-textarea>
</v-row>
</v-card-text>
</v-form>
<v-divider> </v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
large
dark
outlined
color="success"
#click="save"
class="ma-3"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
props: [
"dataRow",
"value",
// ----------------------
],
methods: {
save() {
if (this.$refs.form.validate()) {
this.$root
.$confirm("Are you sure you want to save?")
.then((confirm) => {
if (confirm) {
this.ADD_TOPIC_DATA(this.data)
.then((data) => {
this.FETCH_TOPIC_DATA();
this.$refs.form.reset();
this.$refs.form.resetValidation();
this.close();
})
.catch((err) => {
console.log(err)
});
}
});
}
},
close() {
this.$emit("input", false);
},
}
watch: {
dataRow(val) {
this.data = { ...val };
},
},
Problem I am having is after adding a data, then if I try to add again by opening the dialog, the required field shows validation error, which is name here!
Image of that -->
Searched in stackoverflow. Found that should use this.$refs.form.reset(). Used that in save method without success. Also used this.$refs.form.resetValidation(), but don't work.
Any suggestion?
Thanks in advance.
The problem here is you're assigning new value to dataRow when opening the dialog which triggers validation inside the dialog. You could also use lazy-validation prop which allows you to only manually trigger the validation.

Imported component and console message

I have a problem with props in imported component.
I imported component with dialog template and I want display text from props and it works, but I have errors in console.
In console I see this error:
[Vue warn]: Error in render: "TypeError: Cannot read property
'test' of null"
But after open dialog value is correctly.
How can I fix this problem so that the error in console doesn't show up?
My code:
<template>
<v-container fluid>
<v-row>
<v-col cols="12">
<v-card class="">
<TipItem v-for="tip in tips" :key="tip.id" v-on:open-dialog="openTipDetailsDialog(tip)"></TipItem>
</v-card>
</v-col>
</v-row>
<TipDetailsDialog :dialog="tipDetailsDialog" :tip="tipDetails" v-on:close-dialog="tipDetailsDialog = false"></TipDetailsDialog>
</v-container>
</template>
<script>
import TipItem from "../components/TipItem";
import TipDetailsDialog from "../components/TipDetailsDialog";
export default {
name: "Tips",
components: {
TipItem,
TipDetailsDialog
},
data: () => ({
tips: [],
tipDetailsDialog: false,
tipDetails: null
}),
methods: {
openTipDetailsDialog(tip) {
this.tipDetailsDialog = true;
this.tipDetails = tip;
}
}
};
</script>
TipDetailsDialog
<template>
<v-dialog
:value="dialog"
fullscreen
hide-overlay
transition="dialog-bottom-transition"
#input="$emit('close-dialog')"
>
<v-card class="rounded-0">
<v-toolbar color="primary">
<v-toolbar-title>Szczegóły</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon dark #click="$emit('close-dialog')">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-container>
<v-row>
<v-col cols="6">
<v-card>
{{ tip.test }}
</v-card>
</v-col>
<v-col cols="6">
<v-card>
{{ tip.test2 }}
</v-card>
</v-col>
</v-row>
</v-container>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: "TipDetailsDialog",
props: ["dialog", "tip"]
};
</script>
tipDetails is initialized as null, and then bound to <TipDetailsDialog>.tip, which causes it to try to render tip.test (where tip is null).
Since TipDetailsDialog doesn't really render anything meaningful without a populated tip, that component should probably be conditionally rendered only when tipDetails is truthy:
<TipDetailsDialog v-if="tipDetails" :tip="tipDetails" />
tipDetails is initialized as null, and then bound to
<TipDetailsDialog>.tip, which causes it to try to render tip.test
(where tip is null).
Since TipDetailsDialog doesn't really render anything meaningful
without a populated tip, that component should probably be
conditionally rendered only when tipDetails is truthy:
html <TipDetailsDialog v-if="tipDetails" :tip="tipDetails" />
Or you can set a default value to the prop:
props: {
tip: {
type: String,
required: false,
default: () => ""
},
},

Vue, error [vuex] do not mutate vuex store state outside mutation handlers

Here is original code. Very simple, sign in form. We have Email field, password. It takes this parameters and on clicking submit button it checks the user and writes his user.uid into Vuex. But I'm getting error which is listed above in title. I did research and it looks like it's a common issue in Vuex, due to those fields at some point updating Vuex store 'on a fly' which is false in my case, cause it only updates Vuex store when you press a submit button. Anyhow decided corrected to be look like this and still have no luck
original code
<template>
<div class="form__inner">
<div class="overlay" #click="$store.commit('logReg/logIn')"></div>
<v-container
fill-height
fluid>
<v-row
align="center"
justify="center">
<v-col cols="2">
<v-card>
<v-card-title>
Log in
</v-card-title>
<v-card-text>
<v-text-field placeholder="Email"
v-model="logIn"/>
<v-text-field placeholder="Password"
v-model="password"/>
</v-card-text>
<v-card-actions>
<v-btn color="success"
class="mx-auto"
#click="signIn">Log me in</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
data(){
return {
logIn: '',
password: ''
}
},
methods: {
async signIn(){
let res = await this.$fireAuth.signInWithEmailAndPassword(this.logIn, this.password);
this.$store.commit('userState', res);
}
}
}
</script>
my vuex
export const state = () => ({
user: null
})
export const mutations = {
userState(state, authUser){
state.user = authUser;
}
}
my try to fix issue which still had no luck, gives same error
<template>
<div class="form__inner">
<div class="overlay" #click="$store.commit('logReg/logIn')"></div>
<v-container
fill-height
fluid>
<v-row
align="center"
justify="center">
<v-col cols="2">
<v-card>
<v-card-title>
Log in
</v-card-title>
<v-card-text>
<v-text-field placeholder="Email"
v-model="logIn"/>
<v-text-field placeholder="Password"
v-model="password"/>
</v-card-text>
<v-card-actions>
<v-btn color="success"
class="mx-auto"
#click="signIn">Log me in</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
data(){
return {
logIn: '',
password: ''
}
},
computed: {
logg: {
get(){
return this.logIn;
},
set(val){
this.logIn = val;
}
},
pass: {
get(){
return this.password;
},
set(val){
this.password = val;
}
}
},
methods: {
async signIn(){
let res = await this.$fireAuth.signInWithEmailAndPassword(this.logg, this.pass);
this.$store.commit('userState', res);
}
}
}
</script>
You have to use mutations like this:
<script>
import {mapMutations} from 'vuex';
export default {
data(){
return {
logIn: '',
password: ''
}
},
methods: {
...mapMutations({
userState: 'userState'
}),
async signIn(){
let res = await this.$fireAuth.signInWithEmailAndPassword(this.logIn,this.password);
this.userState(res);
}
}
}
</script>

How do I open and close v-dialog from a component under its parent? Use Vuex?

I need to open a CRUD dialog from a data table component. Both the dialog and data table share the same parent. The data table is reusable but the CRUD dialog is not.
The use case seems very common. An admin page contains a table of data, each row containing an edit button that opens edit dialog.
I've attempted using Vuex below - however this error occurs:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'showUserModal' of undefined"
found in
---> <VBtn>
<VSimpleTable>
<VData>
<VDataTable>
<DataTable> at src/components/DataTable.vue
<Settings> at src/views/Settings.vue
<VContent>
<VApp>
<App> at src/App.vue
<Root>
Why is the imported mutator not available and is this a good approach to achieving the common functionality?
I arrived at my current solution using these 2 approaches
https://markus.oberlehner.net/blog/building-a-modal-dialog-with-vue-and-vuex/
https://forum.vuejs.org/t/how-to-trigger-a-modal-component-from-vuex-store/27243/9
UserAdmin.vue
<template>
<v-container fluid >
<DataTable v-bind:rows="allUsers" v-bind:headers="headers" />
<EditUser />
</v-container>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import DataTable from '../components/DataTable';
import EditUser from '../modals/EditUser';
export default {
name: 'UserAdmin',
methods: {
...mapActions(["getUsers"])
},
computed: mapGetters(["allUsers"]),
components: {
DataTable, EditUser
},
data(){
return {
headers: [
{ text: 'Name', value: 'name' },
{ text: 'Username', value: 'email' },
{ text: 'Administrator', value: 'admin' },
{ text: "", value: "controls", sortable: false}
]
}
},
created(){
this.getUsers();
}
}
</script>
DataTable.vue
<template>
<v-data-table
:headers="headers"
:items="rows"
:items-per-page="5"
class="elevation-1"
>
<!-- https://stackoverflow.com/questions/59081299/vuetify-insert-action-button-in-data-table-and-get-row-data -->
<template v-slot:item.controls="props">
<v-btn class="my-2" fab dark x-small color="blue" #click="onButtonClick(props.item.email)">
<v-icon dark>mdi-pencil</v-icon>
</v-btn>
</template>
</v-data-table>
</template>
<script>
import { mapMutations } from "vuex";
export default {
name: "DataTable",
props:["headers", "rows"],
methods: {
...mapMutations(["toggleUserModal"]),
onButtonClick: function(email) {
console.log("clicked: " + email)
this.toggleUserModal();
}
}
}
</script>
EditUser.vue
<template>
<v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="600px" v-show='showUserModal'>
<v-card>
<v-card-title>
<span class="headline">User Profile</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field label="Legal first name*" required></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field label="Legal middle name" hint="example of helper text only on focus"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
label="Legal last name*"
hint="example of persistent helper text"
persistent-hint
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field label="Email*" required></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field label="Password*" type="password" required></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-select
:items="['0-17', '18-29', '30-54', '54+']"
label="Age*"
required
></v-select>
</v-col>
<v-col cols="12" sm="6">
<v-autocomplete
:items="['Skiing', 'Ice hockey', 'Soccer', 'Basketball', 'Hockey', 'Reading', 'Writing', 'Coding', 'Basejump']"
label="Interests"
multiple
></v-autocomplete>
</v-col>
</v-row>
</v-container>
<small>*indicates required field</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="dialog = false">Close</v-btn>
<v-btn color="blue darken-1" text #click="dialog = false">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
export default {
data: () => ({
dialog: false,
}),
computed: {
showUserModal(){
return this.$store.state.showUserModal
}
}
}
</script>
modals.js
const state = {
showUserModal: false
}
const mutations = {
toggleUserModal: () => (this.showUserModal = !this.showUserModal)
}
const getters = {
showUserModal: state => {
return state.showUserModal
}
}
export default {
state,
getters,
mutations
}
New code based on #Anatoly suggestions - everything works except the events emitted from the dialog, ex: onEditUserConfirmed are not picked up in the parent component.
ModalComponent
<template>
<v-row justify="center">
<v-dialog v-model="visible" persistent max-width="600px">
<v-card v-if="user">
<v-card-title>
<span class="headline">User Profile</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="user.name" label="Legal first name*" required></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field label="Legal middle name" hint="example of helper text only on focus"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
label="Legal last name*"
hint="example of persistent helper text"
persistent-hint
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field label="Email*" required></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field label="Password*" type="password" required></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-select :items="['0-17', '18-29', '30-54', '54+']" label="Age*" required></v-select>
</v-col>
<v-col cols="12" sm="6">
<v-autocomplete
:items="['Skiing', 'Ice hockey', 'Soccer', 'Basketball', 'Hockey', 'Reading', 'Writing', 'Coding', 'Basejump']"
label="Interests"
multiple
></v-autocomplete>
</v-col>
</v-row>
</v-container>
<small>*indicates required field</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="onCancel">Close</v-btn>
<v-btn color="blue darken-1" text #click="onSave">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
export default {
name: "EditUser",
props: {
user: Object,
visible: {
type: Boolean,
default: false
}
},
methods: {
onSave() {
console.log('save button gets here...')
this.$emit("onEditUserConfirmed", this.user);
},
onCancel() {
console.log('cancel button gets here...')
this.$emit("onEditUserCancelled");
}
}
};
</script>
Parent Component
<template>
<v-container fluid>
<v-data-table :headers="headers" :items="allUsers" :items-per-page="5" class="elevation-1">
<!-- https://stackoverflow.com/questions/59081299/vuetify-insert-action-button-in-data-table-and-get-row-data -->
<template v-slot:item.controls="props">
<v-btn class="my-2" fab dark x-small color="blue" #click="onEditClick(props.item)">
<v-icon dark>mdi-pencil</v-icon>
</v-btn>
</template>
</v-data-table>
<EditUser
:user="user"
:visible="isDialogVisible"
#confirmed="onEditUserConfirmed"
#cancelled="onEditUserCancelled"
/>
</v-container>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import EditUser from "../modals/EditUser";
export default {
name: "Settings",
data() {
return {
user: null,
isDialogVisible: false,
headers: [
{ text: "Name", value: "name" },
{ text: "Username", value: "email" },
{ text: "Administrator", value: "admin" },
{ text: "", value: "controls", sortable: false }
]
};
},
methods: {
...mapActions(["getUsers"]),
onEditClick: function(user) {
console.log('Editing user: ' + user.email)
this.user = user;
this.isDialogVisible = true;
},
onEditUserConfirmed(user) {
console.log('Saving user: ' + user.email)
this.isDialogVisible = false;
},
onEditUserCancelled () {
this.isDialogVisible = false;
}
},
computed: mapGetters(["allUsers"]),
components: {
EditUser
},
created() {
this.getUsers();
}
};
</script>
I would not recommend using the state for this task. since it not a very complex scenarios. you should use props and events for handling this kind of scenario
just modify the code a bit.
DataTable.vue
<script>
methods: {
onButtonClick: function(email) {
console.log("clicked: " + email)
this.$emit('openDialog') // or use any name here
}
}
</script>
UserAdmin.vue
<template>
<v-container fluid >
<!-- Listen to the event that you are emitting from DataTable.vue -->
<DataTable :rows="allUsers" :headers="headers" #showDialog="editUser = true" />
<!-- Pass that variable as a Prop -->
<EditUser :showDialog="editUser" />
</v-container>
</template>
<script>
....
data: () => ({
headers: [
{ text: 'Name', value: 'name' },
{ text: 'Username', value: 'email' },
{ text: 'Administrator', value: 'admin' },
{ text: "", value: "controls", sortable: false}
],
editUser: false, // a flag to keep the status of modal.
})
....
</script>
EditUser.vue
<script>
export default {
props: {
showDialog: {
type: Boolean,
default: false
}
},
data: () => ({
dialog: false,
}),
mounted() {
this.dialog = this.showDialog
},
watch: {
showDialog() {
if (this.showDialog)
this.dialog = true
}
}
}
</script>
I hope it should work, it worked for me in my scenario. I won't recommend using the Vuex store in this simple single level structure. Vuex should be used in case of some complex data structures where there are deep layers of components.
The code might have some syntax mistakes (do let me know). but i hope i just conveyed the concept
Use an event in a table component to inform a parent component you wish to edit a user (send a selected user in this event).
Catch the event in a parent component, write a user from the event to a prop in data section and pass this prop to a dialog component.
Use a prop to show/hide dialog from a parent component
Use an event to receive edited user after dialog confirmation.
Something like this:
Parent component
<DataTable v-bind:rows="allUsers" v-bind:headers="headers" #onEdit="onEditUser"/>
<EditUser :user="user" :visible="isDialogVisible" #confirmed="onEditUserConfirmed" #cancelled="onEditUserCancelled"/>
...
data: {
return {
// other data
user: null,
isDialogVisible : false
}
},
methods: {
onEditUser (user) {
this.user = user
this.isDialogVisible = true
},
onEditUserConfirmed (user) {
// hide a dialog
this.isDialogVisible = false
// save a user and refresh a table
},
onEditUserCancelled () {
// hide a dialog
this.isDialogVisible = false
}
}
Table component:
// better send a whole user object insteaf of just e-mail prop? It's up to you
#click="onButtonClick(props.item)"
...
methods: {
onButtonClick: function(user) {
this.$emit('onEdit', user)
}
}
Dialog component:
<v-dialog v-model="visible" ...
// render card only if user is passed
<v-card v-if="user">
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="user.firstName" label="Legal first name*" required></v-text-field>
</v-col>
...
<v-btn color="blue darken-1" text #click="onCancel">Close</v-btn>
<v-btn color="blue darken-1" text #click="onSave">Save</v-btn>
...
export default {
props: {
user: {
type: Object
},
visible: {
type: Boolean,
default: false
}
},
...
methods: {
onSave() {
this.$emit('confirmed', this.user)
},
onCancel () {
this.$emit('cancelled')
}
}
}