I just tried to create a separate form using Vue and vuetify's v-dialog. Unfortunately, once taken into separate component the thing stopped working without any error.
Here's the code to my component:
<template>
<v-dialog :value="dialogOpen" persistent>
<template v-slot:activator="{ props }">
<v-btn color="primary" v-bind="props">Add new</v-btn>
</template>
<v-card>
<v-card-title>
<span class="text-h5">Subscriber</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6">
<v-text-field v-model="activeSubscriber.name" label="Name" required></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field v-model="activeSubscriber.email" label="Email" required></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-select
v-model="activeSubscriber.state"
:items="['active', 'unsubscribed', 'junk', 'bounced', 'unconfirmed']"
label="State*"
required>
</v-select>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue-darken-1" variant="text">
Close
</v-btn>
<v-btn color="blue-darken-1" variant="text" #click="createSubscriber(activeSubscriber)">
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'SubscriberForm',
props: {
id: null,
dialogOpen: false,
},
methods: {
createSubscriber(subscriber) {
this.$store.dispatch('subscribers/createSubscriber', subscriber).then(() => this.dialogOpen = false).then(() => alert(1));
}
},
watch: {
id: function(newId) {
if (newId !== null) {
this.$store.dispatch('subscribers/getSubscriber', newId);
}
},
},
computed: {
...mapGetters('subscribers', ['activeSubscriber']),
}
}
</script>
Basically, the only thing that I changed compared to examples on their website was switch v-model to :Value because we are actually passing the properties to the component.
The component is called like that
<SubscriberForm :id="subscriberId" :dialog-open="dialogOpen"/>
....
methods: {
openEditModal(id) {
this.subscriberId = id;
this.dialogOpen = true;
},
},
I tried watching the property dialogOpen and it gets passed perfectly fine.
What could be the issue?
UPDATE: tried the top voted solution at vuetify: programmatically showing dialog
It resolved the issue, however broke the activator button. So that does not fly as well
Related
I have a project and this project is for the owners of the purchase for the purchase of cars and many other operations, but I have a table with several columns, and within these columns there is a column I listen to action and this column contains a button called Edit and I want when I click on the Edit button to be used The component of the modification within this file, how can I do this?
And it is the Edit file in which the Edit form is located.
Edit.vue:
<template>
<v-row justify="center">
<v-dialog v-model="editDialog" persistent max-width="1050px" height="400px">
<template v-slot:activator="{ on, attrs }">
<v-btn
fab
accent
class="grey lighten-1 margin pa-4"
dark
v-bind="attrs"
v-on="on"
>
<v-icon>
mdi-pencil
</v-icon>
</v-btn>
</template>
<v-card>
<v-layout>
<v-flex xs12>
<div class="myfont pl-5">
<v-card-title>
<span> Edit Car</span>
</v-card-title>
</div>
</v-flex>
</v-layout>
<v-divider xs12></v-divider>
<v-layout>
<v-flex xs12>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
name="name"
label="Name"
id="name"
class="colorLabel"
v-model="editedName"
multi-line
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
name="priceOfSale"
label="Price Of Sale"
id="priceOfSale"
v-model="editedPrice"
class="colorLabel"
multi-line
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
name="numberOfSeats"
label="NumberOfSeats"
id="numberOfSeats"
v-model="editedNumberOfSeats"
multi-line
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
</v-flex>
</v-layout>
<v-divider></v-divider>
<v-layout>
<v-flex xs12>
<v-card-actions>
<v-btn class="myfont pl-5 text-right" text #click="onSaveChanges">
Save
</v-btn>
<v-btn
class="myfont pl-5 text-center"
text
#click="editDialog = false"
>
Cancel
</v-btn>
</v-card-actions>
</v-flex>
</v-layout>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
import { mapActions } from "vuex";
import ActionsTypes from "../store/types/actions-types";
export default {
props: ["car"],
data() {
return {
editedName: this.car.name,
editedPrice: this.car.price,
editedNumberOfSeats: this.car.seatsNumber,
};
},
methods: {
...mapActions({
editCarInformations: ActionsTypes.EDIT_CAR_ACTION,
}),
onSaveChanges() {
const UpdatedCar = { ...this.car };
UpdatedCar.name = this.editedName;
UpdatedCar.price = this.editedPrice;
UpdatedCar.seatsNumber = this.editedNumberOfSeats;
this.editCarInformations(UpdatedCar);
},
},
};
</script>
This file, in which there is a table containing several columns, and the last column is Action, which contains the Modify button, the Modify button, and when I press it, the universe of the amendment is called.
viewAllCars:
<template>
<v-app class="bg">
<v-container>
<v-card
class="mx-auto mt-5 pa-3"
max-width="100%"
id="limited-products"
:style="'border: 0px solid #D50000;'"
>
<v-btn class="red accent-4 color myfont pl-3" #click="onCreateCar">
evict Cashig
</v-btn>
<v-data-table
:headers="tableHeaders"
:items="loadedCarsGetter"
:page.sync="page"
:items-per-page="itemsPerPage"
hide-default-footer
class="elevation-1"
#page-count="pageCount = $event"
>
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete (item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
<!-- pagination -->
<div class="text-center pt-2">
<v-pagination v-model="page" :length="pageCount"></v-pagination>
<v-text-field
:value="itemsPerPage"
label="Items per page"
type="number"
min="-1"
max="15"
#input="itemsPerPage = parseInt($event, 10)"
class="pl-7 pr-7"
></v-text-field>
</div>
</v-card>
</v-container>
</v-app>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import ActionsTypes from "../../store/types/actions-types";
import GettersTypes from "../../store/types/getters-types";
export default {
data() {
return {
page: 1,
pageCount: 0,
itemsPerPage: 10
};
},
created() {},
computed: {
...mapGetters({
loadedCarsGetter: GettersTypes.GET_CAR_FORM_GETTER,
tableHeaders: GettersTypes.GET_HEADERS_TABLE_GETTER,
}),
},
mounted() {
// this.loadedCarsGetter();
this.loadedCarsAction();
},
methods: {
...mapActions({
editcardispatcher: ActionsTypes.EDIT_CAR_ACTION,
deletecardispatcher: ActionsTypes.DELETE_CAR_ACTION,
loadedCarsAction: ActionsTypes.GET_ALL_CAR_ACTION
}),
edit() {
this.editcardispatcher({});
},
delete(){
this.deletecardispatcher(
this.car.id
)
}
},
};
</script>
First of all, you don't need the "v-row" in the Edit.vue. Remove it.
As you have the button as the activator, you should just add the component as Avraham mentioned. But you need to know that there are some caveats with this approach
This is gonna be increasing the memory usage by the browser. As a separate instance of Edit.vue will be added to the DOM for each row in your table.
Each Edit.vue instance will preserve the data in it with the changes that the user might make. And you'll have to handle the data resets.
A better solution would be to add only one instance of Edit.vue and add/remove the component from the DOM using a v-if.
This will keep your table using one instance of Edit.vue, and the addition and removal of the component will handle the data reset.
Something like this
In the file that contains the v-data-table, update the template as follows
<template>
......
<v-data-table ...>
...
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete(item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
...
</v-data-table>
<edit :car="item" v-if="showEditDialog = true" #closed="showEditDialog = false" />
......
</template>
<script>
import Edit from 'Edit.vue'
export default {
components: { Edit },
data: () =({
item: {},
showEditDialog: false,
}),
methods: {
edit(item) {
this.item = item
this.showEditDialog = true
}
}
}
</script>
In your Edit.vue, add a watcher for the "editDialog" property to emit an event to remove the edit dialog from the DOM.
watch: {
editDialog(val){
if(!val)
this.$emit('closed')
}
}
And remove this part from the Edit.Vue
<template v-slot:activator="{ on, attrs }">
<v-btn
fab
accent
class="grey lighten-1 margin pa-4"
dark
v-bind="attrs"
v-on="on"
>
<v-icon>
mdi-pencil
</v-icon>
</v-btn>
</template>
Good luck.
You should import the Edit.vue component in the car viewer component and use it instead of the edit button:
...
<template #[`item.actions`]="{ item }">
<!-- Pass the item to the `car` prop -->
<edit :car="item" />
<v-btn icon #click="delete (item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
...
<script>
import Edit from 'Edit.vue' // make sure the path to the component is correct
export default {
components: { Edit },
...
};
</script>
I am creating my first website with Vue so I am creating a website for saving recipes I have a home page where you click to add new recipes and it takes you to a page where you can write your recipes and it will save on the home page kind of like a ( to do list ), so I am having troubles with saving the data that the user inputs. how can I get back the saved data from getters? ps. I know this is very simple but I am new to Vue
here are my codes (newrecp page) :
<template>
<div class="container">
<v-text-field
class="mx-1 my-1"
label=" food name"
color="black"
outlined
v-model="data . title"
></v-text-field>
<v-timeline :dense=" $vuetify . breakpoint . s m And Down">
<v-timeline-item
color="purple lighten-2"
fill-dot
right
>
<v-card>
<v-card-title class="purple lighten-2">
<h2 class="display-1 white--text font-weight-light">Step 1</h2>
</v-card-title>
<v-container>
<v-row>
<v-col cols="12" md="10">
<v-text area
auto-grow
rows="4"
row-height="20"
shaped
v-model="data.one"
></v-text area>
</v-col>
</v-row>
</v-container>
</v-card>
</v-timeline-item>
<v-timeline-item
color="amber lighten-1"
fill-dot
left
small
>
<v-card>
<v-card-title class="amber lighten-1 justify-end">
<h2 class="display-1 mr-4 white--text font-weight-light">Step 2</h2>
</v-card-title>
<v-container>
<v-row>
<v-col cols="12" md="8">
<v-text area
auto-grow
rows="4"
row-height="20"
shaped
v-model="data. two"
></v-text area>
</v-col>
</v-row>
</v-container>
</v-card>
</v-timeline-item>
<v-timeline-item
color="cyan lighten-1"
fill-dot
right
>
<v-card>
<v-card-title class="cyan lighten-1">
<h2 class="display-1 white--text font-weight-light">Step 3</h2>
</v-card-title>
<v-container>
<v-row>
<v-col >
<v-text area
auto-grow
rows="4"
row-height="20"
shaped
v-model="data .three"
></v-text area>
</v-col>
</v-row>
</v-container>
</v-card>
</v-timeline-item>
<v-timeline-item
color="red lighten-1"
fill-dot
left
small
>
<v-card>
<v-card-title class="red lighten-1 justify-end">
<h2 class="display-1 mr-4 white--text font-weight-light">Step 4</h2>
</v-card-title>
<v-container>
<v-row>
<v-col cols="12" md="10">
<v-text area
auto-grow
rows="4"
row-height="20"
shaped
v-model="data .four"
></v-text area>
</v-col>
</v-row>
</v-container>
</v-card>
</v-timeline-item>
</v-timeline>
<v-layout row wrap>
<v-flex mx-3 >
<v-b t n block color="secondary" dark #click="addnew">Save</v-b t n>
</v-flex>
</v-layout>
</div>
</template>
<script>
export default {
data (){
return{
data: {
title:'',
one: '',
two: '',
three: '',
four: '',
}
},
methods: {
addnew(){
let savedrecp = this.data
this.$store.commit('newrecp', savedrecp)
this.$router.push({ path:'/' })
}},
}
</script>
In my store:
state: {
data : [],
},
mutations: {
newrecp(state, data) {
// mutate state
state. data .push(data)
}
},
getters: {
data(s){
return s .data
},
}
Script for my saved recipe page:
<script>
export default {
data (){
return{
data: {
title: '',
one: '',
two: '',
three: '',
four: ''
},
}
},
computed: {
item(){
return this. $store.getters.data
}
},
mounted() {
console.log(this. data);
},
}
</script>
Home page:
<template>
<div class="home">
<v-container grid-list-xs>
<v-btn bottom fixed to="/new" > Click to add new Recipes
<v-icon>fas fa-home</v-icon>
</v-btn>
<v-layout row wrap>
<v-flex xs12 x13 lg12 v-for="(item, index) in data" :key="index">
<v-card
>
<v-list-item three-line>
<v-list-item-content>
<div class="overline mb-4">Recipe </div>
<h1>{{item.title}}</h1>
<v-list-item-title class="headline mb-1">pizza</v-list-item-title>
<v-list-item-subtitle >Greyhound divisely hello coldly fonwderfully</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-card-actions>
<v-btn class="button3" text>remove</v-btn>
<v-btn class="button1" text :to="'/savedrecp/'+item.title">open</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</v-container>
</div>
</template>
<script>
In general we use two-way bindings on form input, textarea, and select elements inorder to save input data you can read official document here.
regarding you'r code i don't see any problems in it unless there is something that you haven't shared right now you must be able to use you'r data like this:
savedRecipe() {
console.log(this.data) // => you can check you'r data in console.
}
UPDATE
You are trying to modify the vuex state from the vue component, You can not do it. You can only modify vuex store from a mutation.
In you'r store:
mutations: {
addRecipe(state, recipe) {
// mutate state
state.data.push(recipe)
}
and commit to mutation:
savedrecp(){
let savedrecp = this.data
this.$store.commit('addRecipe', savedrecp)
this.$router.push({ path:'/' })
}
I've been trying to push a text field whenever the "add button" is pressed.
This is my code so far.
<v-container fluid>
<v-row>
<v-col cols="7">
<v-row class= "mx-1 my-1">
<v-text-field outlined label="Test 1" v-model="test1"></v-text-field>
</v-row>
</v-col>
<v-col cols="5">
<v-row class= "mx-4 my-1">
<v-text-field type="number" outlined label="Test 2" v-model="test2"></v-text-field>
</v-row>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-row class= "mx-1 my-n8">
<v-btn
#click="doWork()"
width = 100%
small
outlined
color="#0000b0"
>
<v-icon dark>mdi-plus</v-icon></v-btn>
</v-row>
</v-col>
</v-row>
</v-container>
Here is my javascript
<script>
export default {
data () {
return {
test1:'',
test2:''
}
},
methods: {
doWork () {
//just for debugging purposes
console.log(this.valor)
console.log(this.precio)
}
}
}
</script>
What should I add in the "doWork()" method in order to push another pair of text fields
Thanks in advance <3
You can transform your fields into a fields array:
data () {
return {
inputs: []
}
}
Your addWork method becomes a method that push a new value in this inputs array:
methods: {
doWork () {
this.inputs.push({
label: 'Test',
value: ''
})
}
}
And you display those inputs with a v-for directive:
<v-row>
<v-col cols="2" v-for="(input, index) in inputs" :key="index">
<v-row class="mx-1 my-1">
<v-text-field outlined :label="input.label" v-model="input.value"></v-text-field>
</v-row>
</v-col>
</v-row>
Working example: https://codesandbox.io/s/festive-dream-gbo6t?file=/src/App.vue
I want to center an image (passed as props) in a dialog and the size of the dialog should be the same as the image. Maybe with some offset because of the toolbar with the x to close the dialog.
Does someone know how to remove the scrolling and set the correct size for the dialog?
Code
<template>
<v-dialog
v-model="showDialog"
:height="picture.PicHeight"
:width="picture.PicWidth"
>
<v-card>
<v-toolbar dense color="elevation-0">
<v-spacer></v-spacer>
<v-btn icon color="black" #click.native="closePictureDialog">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-row no-gutters>
<v-col cols="12">
<v-row align="center" justify="center">
<v-img
:src="picture.Pic"
:height="picture.PicHeight"
:width="picture.PicWidth"
contain
></v-img>
</v-row>
</v-col>
</v-row>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: "PictureDialog",
props: {
showDialog: {
type: Boolean,
default: false
},
picture: {
type: Object,
default: () => {}
}
},
data() {
return {
//
};
},
methods: {
closePictureDialog() {
this.$emit("onClosePictureDialog");
}
}
};
</script>
Best regards,
A.Mehlen
The scrolling is happening because you need no-gutters on the inner <v-row>...
<v-dialog
v-model="showDialog"
:height="picture.PicHeight"
:width="picture.PicWidth">
<v-card>
<v-toolbar dense color="elevation-0">
<v-spacer></v-spacer>
<v-btn icon color="black" #click.native="closePictureDialog">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-row no-gutters>
<v-col cols="12">
<v-row no-gutters align="center" justify="center">
<v-img
:src="picture.Pic"
:height="picture.PicHeight"
:width="picture.PicWidth"
contain
></v-img>
</v-row>
</v-col>
</v-row>
</v-card>
</v-dialog>
https://codeply.com/p/hFIuHqYRFk
I am working on Vuetify.js and I am a newbie in Vue, I referred this document Vuetify Dialogs for creating dialog and solution of Matheus Dal'Pizzol from this link Open a Vuetify dialog from a component template in VueJS
to separate it to the component.
The result I have child component as dialog as below
Parent
<template>
<v-container fluid>
<v-row dense>
<v-col cols="12">
<v-btn large color="success">Add product</v-btn>
</v-col>
<v-col cols="3" v-for="product in products" :key="product.id">
<v-card class="mx-auto" outlined>
<v-list-item three-line>
<v-list-item-content>
<v-list-item-title class="headline mb-1">{{product.name}}</v-list-item-title>
<v-list-item-subtitle>{{product.title}}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-card-actions>
<v-btn dark color="cyan" #click.stop="showScheduleForm = true">
<v-icon dark>mdi-television</v-icon>
</v-btn>
<v-btn color="primary">Detail</v-btn>
</v-card-actions>
</v-card>
<modal-detail v-model="showScheduleForm" :productDetailInfo="product"></modal-detail>
</v-col>
</v-row>
</v-container>
</template>
<script>
import axios from "axios";
import ModalDetail from "./ModalDetail.vue";
export default {
name: "HelloWorld",
components: {
ModalDetail
},
data: function() {
return {
showScheduleForm: false,
products: [],
errors: []
};
},
...
Child
<template>
<v-dialog v-model="show" max-width="500" v-if="Object.keys(productDetailInfo).length !== 0">
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>{{ productDetailInfo.name }}</v-card-title>
<v-card-text>{{ productDetailInfo.title }}</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click.stop="show = false">Close</v-btn>
<v-btn color="primary">Detail</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: "ModalDetail",
props: {
productDetailInfo: {
type: Object
},
value: Boolean
},
computed: {
show: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
}
}
}
};
</script>
However, I am getting an exception when I click icon-button "Maximum call stack size exceeded".
It seems there is a continuous loop happening.
Please help! Am I missing something?
That's because your v-dialog is in v-for loop, it's common problem. To workaround it add :retain-focus="false" as a prop to your v-dialog
Maybe try to use v-on:click.stop instead of #click.stop in the v-btn as it's the recommended syntax for Vue 2.x.