How to call submit.prevent on child component - vue.js

I've got a parent component with a submit button
<v-card flat>
<child-component :submit="submit()"></child-component>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn #click="submit" type="submit">Change password</v-btn>
</v-card-actions>
</v-card>
and a child component with a form that has a #submit.prevent that I need to call whenever I click the button on the parent:
<v-form #submit.prevent="submit">....</v-form>
My submit() is on the parent, it's just a simple function that submits the data to a database. It requires parameter X.
The reason I have a #submit.prevent on the child form is that I can press enter to submit the form.
How can I pass the submit() as a prop to the child? Or how can I call submit from the child component?

The child should emit an event on submit:
<v-form #submit.prevent="$emit('submit')">....</v-form>
And the parent can listen for that event to call the method:
<child-component #submit="submit"></child-component>

You can use slots to pass your submit button from parent to child form and it will work. Also you can define default template, if no slots is passed.
So you will be able to use form itself, without passed submit button (it will have it's own, default, button), or pass slot with any button you need.
Then you just listen to submit event of form (submit.prevent on form) and do whatever you want in parent component.
Vue.component('form-component', {
template: `
<form #submit.prevent="submitEvent">
<input type="text" v-model="value"/>
<slot>
<button type="submit"> Default Submit </button>
</slot>
</form>
`,
data: () => ({
value: 'Hello Vue!'
}),
methods: {
submitEvent(e) {
this.$emit('submit', e);
}
}
});
const app = new Vue({
el: '#app',
methods: {
submitForm(e) {
console.log(e.target);
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form-component #submit="submitForm">
<template v-slot>
<button type="submit"> Submit </button>
</template>
</form-component>
</div>

You can use ref
<v-card flat>
<child-component ref="form"></child-component>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn #click="submit" type="submit">Change password</v-btn>
</v-card-actions>
</v-card>
methods: {
submit() {
this.$refs.form.submit()
}
},

Related

<v-dialog> value prop doesn't work when extending component

I created a FormDialog component which extends v-dialog
FormDialog
<template>
<div>
<v-dialog
v-bind="$props"
:value="dialog"
:transition="false"
scrollable
persistent
no-click-animation
#keydown.ctrl.enter="handleSave"
#keydown.esc="handleCancel"
#click:outside="handleCancel"
>
<v-card>
<v-card-title>
<!-- title -->
</v-card-title>
<v-card-text>
<v-container>
<slot name="form">
missing form slot
</slot>
</v-container>
</v-card-text>
<v-card-actions>
<!-- actions -->
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { VDialog } from 'vuetify/lib'
export default {
extends: VDialog,
props: {
/* Custom props */
/* Direct binding to value prop doesn't work */
dialog: undefined
},
methods: {
handleSave () {
this.$emit('save')
},
handleCancel () {
this.$emit('cancel)
}
}
}
</script>
As you can see I'm using a prop named dialog binded to v-dialog value prop, because direct value usage doesn't work.
ChildComponent (using value, doesn't work)
<FormDialog
:value="showDialog"
#save="handleSave"
#cancel="handleCancel"
>
<!-- form -->
</FormDialog>
Obviously I removed dialog prop references.
ChildComponent (using dialog, works)
<FormDialog
:dialog="showDialog"
#save="handleSave"
#cancel="handleCancel"
>
<!-- form -->
</FormDialog>
Do anyone knows the answer? I'd like to use value prop directly.

Close v-edit-dialog from method within script tag

I have v-edit-dialog with custom slot. I want the v-edit-dialog to close from a method which is triggered by a button inside the input slot of the dialog.
At the moment I have to press outside the dialog for closing it
I searched all over, Did not found anything regarding that specific problem
<v-edit-dialog
#click.native.stop
>
{{ channel.Options}}
<template v-slot:input>
<v-select
ref="option-select"
:data-key="index"
:items="index == 0 ? options: [...options, 'Disable Channel']"
>
</v-select>
<v-col class="text-right pa-0 ma-0 mb-2">
<v-btn color="primary" outlined #click="handleOptionChange(index)"
>Apply</v-btn
>
</v-col>
</template>
</v-edit-dialog>
<script>
export default {
...
methods: {
handleOptionChange(index){
...
//Close v-edit-dialog
}
}
}
</script>
I don't see an explicit API to close the dialog.
A workaround is to programmatically click the table to simulate an outside-click, which would automatically close the dialog:
<template>
<v-data-table ref="myTable">
<template v-slot:input>
<v-btn #click="closeDialog">Close</v-btn>
</template>
</v-data-table>
</template>
<script>
export default {
methods: {
closeDialog() {
// simulate outside-click to close edit-dialog
this.$refs.myTable.$el.click()
},
}
}
</script>
demo
I tried the click outside method and it doesn't work 100% of the time.
I found a better way to do it by referencing the edit dialog and then changing its internal property isActive to false:
<v-edit-dialog ref="editDialog">
<template v-slot:input>
<v-btn #click="closeDialog">Close</v-btn>
</template>
</v-edit-dialog>
<script>
export default {
methods: {
closeDialog() {
this.$refs.editDialog.isActive = false;
}
}
}
</script>

How to open popup modal present in parent component on clicking button present on child component?

I have a Parent and Child component
Parent Component : contains the modal
Child Component: contains button to toggle modal
how to open a modal on clicking button present in child component
Button to open modal inchild component
<button class="btn btn-primary" #click="openModal()">Click to Open</button>
Modal in Parent Component
<b-modal
id="bv-modal-lead"
header-class="py-2 bg-primary text-white"
body-class="p-0"
hide-footer
style="z-index:9999;"
size="md"
no-close-on-esc
no-close-on-backdrop
header-close-variant="light">
<template v-slot:modal-title>Required Details</template>
<div class="d-block p-0">
<ChildCopmonent
#openModal="clickToOpen"/>
</div>
</b-modal>
//Parent
<script>
export default {
methods:{
openModal(){
this.$bvModal.show('bv-modal-lead')
}
}
}
</script>
How to open parent modal from clicking button present in child component
You can emit an event in child component and fetch it in the parent one.
So in the child component there should be #click="$emit('openModalEvent')" where openModalEvent is the event name (it can be openModal but remember it's an event not a method).
Now You can listen for openModalEvent event in the parent component - #openModalEvent="openModal" where openModal is a method to call.
Child
<button class="btn btn-primary" #click="$emit('openModalEvent')">Click to Open</button>
Parent
<b-modal
id="bv-modal-lead"
header-class="py-2 bg-primary text-white"
body-class="p-0"
hide-footer
style="z-index:9999;"
size="md"
no-close-on-esc
no-close-on-backdrop
header-close-variant="light">
<template v-slot:modal-title>Required Details</template>
<div class="d-block p-0">
<ChildCopmonent #openModalEvent="openModal" />
</div>
</b-modal>
Vue.component('message', {
template: '<div class="message" #click="handleClick">Click to Dismiss Message</div>',
methods: {
handleClick() {
this.$emit('click')
}
}
})
new Vue({
el: '#app',
data: {
showMessage: true
},
methods: {
toggleMessage () {
this.showMessage = !this.showMessage
}
}
})
check this fiddle https://jsfiddle.net/vyq75n0d/

how can i call modal component inside a v-for loop?

I use vuetify https://vuetifyjs.com/en/
My parent component like this :
<template>
<v-container>
...
<v-col
v-for="(item, i) in items"
:key="i"
>
<v-card
>
<v-app-bar dark color="grey">
<v-toolbar-title>Weekly Schedule : {{item.name}}</v-toolbar-title>
<div class="flex-grow-1"></div>
<modal-datepicker :schedule="item" />
</v-app-bar>
</v-col>
...
</v-container>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
};
</script>
My child component like this :
<template>
<v-dialog
:ref="dialog"
v-model="modal"
:return-value.sync="date"
>
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">Show datepicker</v-btn>
</template>
<v-row justify="center">
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary">OK</v-btn>
</v-date-picker>
</v-row>
</v-dialog>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
props: ['schedule'],
data: () => ({
date: new Date().toISOString().substr(0, 10),
modal: false,
}),
mounted() {
console.log(this.schedule)
},
};
</script>
The code above works, but seems inefficient. because every time the loop, it call modal dialog. I want modal to only be called when user click show datepicker button
how do i do that?
Apologies as I am on my mobile. Have you considered having the modal in your parent component outside of the loop?
You could have a data variable to handle your modal e.g
modal: {
open: false,
schedule: null }
Essentially then when your button is clicked you could add a v-click with a function that controls this data which in turns handles the content in the modal.
Then on your date picker or modal have a function to handle the update/close to clear this data and handle whatever you need too outside.
Then you could combine this into one component.
I see you have vuex you could also use vuex to control your modal and it’s contents.

how we can go to child components in next button and back to parent components prev button in vuejs

I have 2 components a parent and a child. I want to show child component on parent's next button and parents component on child's previous button.
How can i do this?
Now I can call child component in next button but if i click the child component's prev button then parent component is not coming. How can i call parent component?
This is my parent component
<template>
<v-form>
<v-flex>
<div v-if="response.inputType == 'radio'" v-model="response.next" class="radio radio-success"
v-bind:id="response.id" >
<input type="radio" v-bind:id="'option'+ response.id" v-bind:name="index"
v-bind:next="response.next"
v-bind:rules="[v => !!v || 'You must agree to continue!']" v-bind:value="response.option" v-model="question.userResponses"
v-on:click="updateResponses" v-bind:checked="question.userResponses">
<label v-bind:for="'option'+ response.id" >
{{response.option}}
</label>
</div>
<v-btn color="info" v-bind:id="question.prev" v-if="currentQuestion > 0"
v-on:click="goToPreviousQuestion(e)" >prev</v-btn>
<v-btn color="success"
v-on:click="goToNextQuestion()">Next</v-btn>
</v-flex>
</v-form>
<child v-if="showsubchild">
</child>
</template>
<script>
import child from '../components/child'
components: {
child
}
methods: {
goToNextQuestion() {
this.showsubchild=true;
}
}
</script>
and this is my child component
<div>
<div v-if="response.inputType == 'radio'"
v-model="response.next"
class="radio radio-success"
v-bind:id="response.currentQuestion" >
<input type="radio"
v-bind:id="'child'+ response.currentQuestion"
v-bind:name="index"
v-bind:value="response.option" v-model="question.userResponses"
v-bind:checked="question.userResponses">
<label v-bind:for="'child'+ response.currentQuestion" >
{{response.option}}
</label>
</div>
<v-btn color="info"
v-on:click="prev(e)" >
prev
</v-btn>
<v-btn color="success"
v-on:click="next()">
Next
</v-btn>
<main if="showPerent"></main>
</div>
<script>
import main from '../components/survey'
export default {
name: "create",
props: ['my-props'],
components: {
main,
},
methods: {
next(){
this.showPerent=true,
}
}
}
</script>