Run method when Vuetify dialog is opened - vue.js

I have a v-data-table and the user can click on any row and a dialog opens. Inside on my vuetify dialog is a dropdown of data.
I want to filter this data everytime the user clicks on a row and filter out what the user clicked from the dropdown inside the dialog.
Here is my dialog:
<v-dialog v-model="edit" max-width="1200" #input="closeDialog()">
<editCompany :isEdit="true"
v-if="showEdit"
:company="selected"
:adminEdit="true"></editCompany>
</v-dialog>
You can see I'm passing in the values from the table row the user clicked.
Now, I need to use the value being passed in to filter the dropdown. The mounted event only runs once, so my logic inside of it only fires for the first row clicked.
Here is the mounted event inside of my editCompany template:
mounted: async function () {
this.filterOutResults(); //this is where i do my filtering and it works
},
Every other row the user clicks doesn't fire mounted, so I cant use that unless I can unmount the dialog when its closed.
I found how to fire an event when the dialog closes, but I cannot find a vuetify open event.
How do I run a function everytime the dialog opens so I can filter the results or how do I unmount the dialog everytime it closes, so the mounted event can run everytime? Thanks

For future references, I'll expand #mynd comment, which should be the answer:
export default {
data() {
return {
dialogVisible: false,
},
},
watch: {
dialogVisible(visible) {
if (visible) {
// Here you would put something to happen when dialog opens up
console.log("Dialog was opened!")
} else {
console.log("Dialog was closed!")
}
}
},
<v-dialog v-model="dialogVisible" max-width="1200" #input="closeDialog()">
<!-- Add code here, according to Vuetify docs -->
</v-dialog>
For further information (about constructing the v-dialog component itself), refer to the official docs

if you want to do something inside the dialog component when it's open, there is a little workaround that you can do.
<v-dialog v-model="edit" max-width="1200" #input="closeDialog()">
<editCompany :isEdit="true"
v-if="showEdit"
:company="selected"
:dialogOpen="edit"
:adminEdit="true"></editCompany>
</v-dialog>
as you see you can pass dialog's handler variable as dialog's parameter. its 'dialogOpen' in this case.
then inside editCompany component,
watch: {
'dialogOpen' :{
handler(newVal, oldVal) {
//do your stuff.
},
deep: true
},
}
So in short it will watch the variable which is controlling dialog, based on its value which mostly is true or false, you can do your operation.

Related

Should I worry about memory leaks due to not removing event handlers?

I recently had to add event handler to vuetify dialog's overlay and it works.
Opening / closing dialog is controlled by a boolean variable dialog:
<v-dialog v-model="dialog">
<MyComponent v-if="dialog" />
</v-dialog>
And some codes to add / remove handler:
openDialog() {
this.dialog = true
setTimeout(() => {
const overlay = document.querySelector('.v-overlay--active .v-overlay__scrim')
if (overlay) {
overlay.addEventListener('my-event', this.myEventHandler)
}
}, 100)
},
closeDialog() {
const overlay = document.querySelector('.v-overlay--active .v-overlay__scrim')
if (overlay) {
overlay.removeEventListener('my-event', this.myEventHandler)
}
this.dialog = false
},
My concern is that, as closeDialog is not always called (e.g. when clicking outside of the dialog), do I need to worry about memory leaks in this case?
The dialog overlay seems like getting inserted / deleted by vuetify library and I'm not sure if these changes will make trouble or not.
My question was quite vuetify specific. I didn't know the existence of event click:outside available for v-dialog. Also I had to make the dialog persistent and handle keyup event for esc to dismiss the dialog:
// #keyup.esc="handleEsc" somewhere close to top
<v-dialog v-model="dialog" persistent #click:outside="closeDialog">
<MyComponent v-if="dialog" />
</v-dialog>
So now I'm explicitly handling all events to dismiss the dialog and confident that no need to worrying about memory leaks.

Bootstrap Vue - Prevent dropdown close on click outside

I'm using Bootstrap-Vue and I would like my dropdown to never hide until a button calls a function. My issue is that, by default, clicking outside the dropdown or on the dropdown button hide the dropdown. My template is:
<b-dropdown
ref="dropdown"
#hide="onEditControlMenuHide"
#click.native.stop>
<b-dropdown-form>
<b-dropdown-item-button
#click="closeDropdown"
>I'm a button
</b-dropdown-item-button>
</b-dropdown-form>
</b-dropdown>
And then I have simple methods in my script:
closeDropdown() {
this.$refs.dropdown.hide()
},
onEditControlMenuHide(bvEvent) {
bvEvent.preventDefault()
},
I tried to catch the bvEvent in a function to process where the event comes from, but I find nothing in the event that differentiate hide on click outside, hide on click the dropdown button, or hide because on my custom button.
I read that it was maybe because of event bubbling, so I tried to use #click.native.stop but it doesn't work. Any help would be appreciated!
You could set a flag that is only set to true by the button's click-handler, then the hide-handler would only cancel the hide if the flag is set:
export default {
methods: {
closeDropdown() {
this._okToHide = true
this.$refs.dropdown.hide()
},
onEditControlMenuHide(bvEvent) {
if (this._okToHide) {
this._okToHide = false
} else {
bvEvent.preventDefault()
}
},
}
})
demo
<b-dropdown-item-button
#click.stop="closeDropdown"
>I'm a button
</b-dropdown-item-button>
try to use .stop modificator with click event on ur button and not on dropdown, it stops propagination event from button and should not trigger click event on dropdown.

Execute method of the embeded component with Vue js

I have a vuejs component("Main") with a dialog and I use a subcomponent("SpeechToText") into it.
When I´m going to open the dialog, I need to check if "speechInititalized" of the "SpeechToText " is true.
if yes, I´d like to call a "reset method" to stop the microphone and the user can restart as if were the first time.
How could I do that?
Here is a snippet of the code.
//Main component
<template>
<div>
<v-dialog v-model="dialogSpeech" hide-overlay persistent width="700">
<SpeechToText></SpeechToText>
<v-btn color="info" #click="closeDialogSpeech">Fechar</v-btn>
</v-dialog>
</div>
</template>
data:()=>({
speechInititalized:false,
}),
methods:{
closeDialogSpeech(){
this.dialogSpeech = false;
}
openDialogSpeech(){
//I´d like to call reset method of the SpeechToText component if it was initialized
if (speechInititalized){ //from SpeechToText data
reset(); //from SpeechToText data
}
}
}
//SpeechToText
data:()=>({
speechInititalized:false,
})
mounted(){
this.initialize();
}
methods{
initialize(){
//speech api is initialized
speechInititalized=true;
};
reset(){
//speech api is stopped
//stop microphone
//clear data
};
}
Don't check if speech was initialized in parent component. That's child component's responsibility. All you do in parent component is emit/announce the dialogue has opened.
Child component reacts to this announcement.
Proof of concept:
// parent
<template>
...
<speech-to-text :is-dialogue-open="isDialogueOpen" />
...
</template>
<script>
export default {
...
data: () => ({
isDialogueOpen: false // set this to true/false when dialogue is opened/closed
}),
...
}
// child:
export default {
props: {
isDialogueOpen: {
type: boolean,
default: false
}
},
...,
watch: {
isDialgueOpen(value) {
// reset if dialogue has just opened and speech has previously been initialized
if (value && this.speechInitialized) {
this.reset();
}
}
},
...
}
Another, more flexible and cleaner approach, preferable when the relation between parent and child is not as direct, or even dynamic and, generally, preferable in larger scale applications, is to use an eventBus (basically a singleton shared across components for emitting/listening to events).
Emit an event on the bus in any corner of the application and have as many listeners reacting to that event in as many other components in the app, regardless of their relation to the original emitter component.
Here's a neat example explaining the concept in more detail.
If you're using typescript, you might want to give vue-bus-ts a try.
This approach is similar to the previous one (emit an event when dialogue is opened and react to it in SpeechToText component), except both parent and child are now cleaner (none of them needs the isDialogueOpen prop, and you also get rid of the watch - whenever possible, avoid watch in Vue as it's more expensive than most alternatives).
Since the event listener is inside SpeechToText, you can check if speech has already been initialized.
Another benefit of this approach is that you don't have to emit/change anything when dialogue closes (unless you want to react to that event as well).

v-select on close event or remove validation error timer?

Final Edit/Solution: https://jsfiddle.net/up9xkhsm/1/
Is there such an event for v-select that I can key on when it is closed? Or some sort of a 'timer' I can set to remove validation errors after they occur?
This is the v-select I am using:
https://vuetifyjs.com/en/components/selects
Edit: this outlines the issue:
https://jsfiddle.net/96vnLm7g/
I want to know when a user clicked on the v-select but did not select anything. It has to be possible, obviously, since the validation can pick up on this..
Use an onChange prop to add your callback function, so you can check for the v-model assigned to v-select if has changed, to clear the validation errors. Or watch the v-model assigned to v-select for changes.
Using onChange:
<v-select :options="options" :on-change="cleanUpValidation" v-model="selectModel" name="some-select"></v-select>
And in VueJS
methods: {
cleanUpValidation(){
//do the cleanup
}
}
By default, onChange emits input event with value of selected option:
default: function (val) {
this.$emit('input', val)
}
So you can use it also to catch the input event:
<v-select :options="options" #input="cleanUpValidation" v-model="selectModel" name="some-select"></v-select>
In VueJS
methods: {
cleanUpValidation(val){
//do something with selected option value or cleanup error
}
}
Or you can watch the model assigned to v-select:
watch: {
'selectModel' : function(){
//do the cleanup or something with this.selectModel
}
}
For onChange and other props see:
https://sagalbot.github.io/vue-select/docs/Api/Props.html
Same thing would apply for VuetifyJS's v-select.
Edit:
Main goal was to clear validation errors when v-select is actually clicked.
v-select uses focus event within its onClick() method, to tell the VueJS that component is clicked, so that can be used to catch the click event:
<v-select
#input="inputChanged"
v-on:change="changeChanged"
label="Select Item"
:items="myItems"
required
:rules="rules.requiredField"
#focus="focusChanged"
>
</v-select>
And in js:
methods:{
focusChanged(){
console.log('focusChanged ');
},
}
For last example: https://jsfiddle.net/c5moqweu/
And see https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VSelect/VSelect.js
onClick
Method

Open a modal and fetch data from server on click of a button outside the component

I am very new to Vue.js. In fact I just started today.
I have a problem.
Let's say I have button in a table somewhere in dom.
<table>
<tr><td><button v-on:click="showModal">Show</button>
</table>
Now I have a modal box outside of the scope of the button.
This button is inside a component of itself and the modal box has a component of itself too.
I am passing in an id with this button, and what I want to do is:
Fetch the id on button click
Show the record fetched in the modal and then finally perform some action on it
My problem is I am unable to get a method in the Modal component (that does a http request and fetches and renders the data) to trigger by the click event of this button.
The button and the modal has no relationship, they are not parent/child.
In modal component trigger method to fetch data by component ready state:
ready: function() {
this.getAllTheDataYouNeed();
},
You may use another life cycle hook:
https://vuejs.org/guide/instance.html#Lifecycle-Diagram
An option was to add the event broadcast in the common ancestor of both the components.
Like this:
var main = new Vue({
el: 'body',
components: {
zmodal : zmodal,
showhidebtn : showhidebtn,
},
methods: {
showModal: function (currentId) {
this.$broadcast('openModalBox', currentId);
}
}
});
Add an event listener in 'zmodal' and call this function showModal from on click event of 'showhidebtn' component.
It is working but now I have a set of codes outside the components that have to be triggered for this to work.
I wonder if there is a better way to do this.