I'm taking the first steps with Quasar.
I want to build a modal window to be reused in forms.
I am using Dialog plugin and q-layout in a custom component.
However, when I use this custom component in another component the dialog plugin methods do not work.
Is there any way to solve this?
util/modal.js
import { Dialog } from "quasar";
export function ModalWindow(CustomComponent) {
Dialog.create({
component:CustomComponent,
ok: {
push: true
},
cancel: {
color: 'negative'
},
persistent: true
})
}
modal/ModalWindow.vue (custom component):
<template>
<q-dialog persistent ref="dialog" #hide="onDialogHide">
<q-layout view="lhh LpR lff" container style="height: 400px" class="bg-grey-3">
<q-toolbar class="bg-primary text-white q-mb-sm">
<q-toolbar-title>
<slot name="titelWindow"></slot>
</q-toolbar-title>
<q-btn v-close-popup flat round dense icon="close" />
</q-toolbar>
<q-form #submit.prevent="submitForm">
<q-card-section>
<slot></slot>
</q-card-section>
<q-card-actions align="right">
<slot name="toolbarBottom"></slot>
</q-card-actions>
</q-form>
</q-layout>
</q-dialog>
</template>
<script>
export default {
methods: {
show () {
this.$refs.dialog.show()
},
hide () {
this.$refs.dialog.hide()
},
onDialogHide () {
this.$emit('hide')
}
}
}
</script>
Call method ModalWindow on a page:
<script>
import { ModalWindow } from 'src/util/modal'
import CustomComponent from "components/modal/MyModalWindow.vue"
export default {
methods: {
showUWin: function(id) {
ModalWindow(CustomComponent)
}
}
}
</script>
So far it works well.
However, as I said,when I use this custom component in another component the dialog plugin methods do not work.
render custom component in another component: MyModalForm.vue
<template>
<MyModalWindow>
<!--Dialog's show method doesn't work-->
</MyModalWindow>
</template>
<script>
export default {
name: 'MyModalForm',
components: {
'MyModalWindow': require('components/modal/MyModalWindow.vue').default,
}
}
</script>
Call method ModalWindow on a page:
<script>
import { ModalWindow } from 'src/util/modal'
import CustomComponent from "components/modal/MyModalForm.vue"
export default {
methods: {
showWin: function(id) {
ModalWindow(CustomComponent)
}
}
}
</script>
I get on de console:
[Vue warn]: Error in mounted hook: "TypeError: this.$refs.dialog.show
is not a function"
I recently got into the same error.
My understanding is that, when you use something like:
Dialog.create({
component: CustomComponent,
...
})
// or
this.$q.dialog({
component: CustomComponent
})
the CustomComponent must directly implement the required show/hide/... methods, as per documentation.
So you have to repeat in each custom component this code (adapting it to the right "ref", specific to your component):
methods: {
show () {
this.$refs.dialog.show()
},
hide () {
this.$refs.dialog.hide()
},
onDialogHide () {
this.$emit('hide')
}
}
and propagate onOk and onCancel events appropriately.
For instance, summarizing everything:
<template>
<MyModalWindow ref="myModalForm" #ok="onOk" />
</template>
<script>
export default {
name: 'MyModalForm',
components: {
'MyModalWindow'
},
methods: {
show() {
this.$refs.myModalForm.show();
},
hide() {
this.$refs.myModalForm.hide();
},
onHide() {
this.$emit('hide');
},
onOk() {
this.$emit('ok');
this.hide();
}
}
}
</script>
Related
if i have component LogoutButton.vue
export default {
data()
{
return {
}
},
methods:
{
Logout()
{
console.log("something")
}
}
}
</script>
<template>
<button #click="Logout">Logout</button>
</template>
How can i use the LogoutButton component inside other component?
I mean how can i import the component and its click methods inside other component?
<script>
import LogoutButton from './LogoutButton.vue'
export default {
components: {
LogoutButton
},
data()
{
return {
}
},
methods:
{
// Your different methods
}
}
</script>
<template>
<div>
<!-- The template from LogoutButton component will display here -->
<LogoutButton />
</div>
</template>
I'm using the 'vue-lottie' package and there's not much information about how to use it.
I got the JSON animations from Lordicons and it shows correctly but I can't make the animation work on hover or click, only loop or static (no animation).
My Component:
<template>
<div>
<lottie
:options="lottieOptions"
:width="50"
/>
</div>
</template>
<script>
import lottie from "vue-lottie/src/lottie.vue";
import * as animationData from "~/assets/about.json";
export default {
components: {
lottie
},
data() {
return {
anim: null, // for saving the reference to the animation
lottieOptions: {
animationData: animationData.default,
loop: false,
autoplay: false,
}
};
},
methods: {
handleAnimation(anim) {
this.anim = anim;
},
stop() {
this.anim.stop();
},
play() {
this.anim.play();
},
pause() {
this.anim.pause();
},
}
};
</script>
And I'm using on the page importing the component only:
...
<AboutIcon />
...
<script>
import AboutIcon from "~/components/AboutIcon.vue";
export default {
components: {
AboutIcon,
},
data() {
return {};
}
};
</script>
From the code sample you provided, you forgot to attach the #animCreated="handleAnimation" event.
So this.anim is actually always null.
<template>
<div>
<lottie
:options="lottieOptions"
:width="50"
#animCreated="handleAnimation"
/>
</div>
</template>
Then you just have to set a #mouseover="play" to start the animation on hover.
I am trying to implement sign out handling in Vue. I redirect to Home which works fine on all pages except Home which is not refreshed. So I decided to emit a signal and refresh data once I catch it.
App.vue
<b-dropdown-item href="#0" v-on:click="signMeOut()">Sign out</b-dropdown-item>
methods: {
signMeOut() {
this.$store.dispatch('SIGN_USER_OUT');
if (this.$route.path === '/') {
this.$emit('sign-out');
} else {
this.$router.push({ name: 'home' });
}
},
Home.vue
<b-container fluid="true" class="pt-3 w-75 m-auto" v-on:sign-out="reload">
created() {
this.$store.dispatch('INIT_STREAM');
},
methods: {
reload() {
console.log('reload');
this.$store.dispatch('INIT_STREAM');
},
},
but the signal does not reaches the Home.vue or is ignored. How can I fix it? Or do you have a better solution of this sign out procedure?
When you use the hook $emit.
You should listen to this event in $root instance from your vuejs application, $root.
So for achieve the desired result you just have to change your code to:
In your component home (I'm putting only the session script from a .vue file)
<script>
export default {
name: 'Home',
components: {
HelloWorld
},
created(){
this.$root.$once('mylogouthandler', this.logoutEventHandler)
},
methods: {
logoutEventHandler() {
console.log('exit')
//do your stuff here.
}
}
}
</script>
your component with action logout.
<template>
<div class="about">
<button #click="handleButtonClick()">logout</button>
</div>
</template>
<script>
export default {
name: 'About',
methods: {
handleButtonClick(){
console.log('clicked')
this.$root.$emit('mylogouthandler')
}
}
}
</script>
If you would like to know more, here is the documentation for handling events in vuejs.
I'm building web app with Vue, Nuxt, and Element UI.
I have a problem with the Element dialog component.
It can open for the first time, but it can't open for the second time.
This is the GIF about my problem.
https://gyazo.com/dfca3db76c75dceddccade632feb808f
This is my code.
index.vue
<template>
<div>
<el-button type="text" #click="handleDialogVisible">click to open the Dialog</el-button>
<modal-first :visible=visible></modal-first>
</div>
</template>
<script>
import ModalFirst from './../components/ModalFirst.vue'
export default {
components: {
'modal-first': ModalFirst
},
data() {
return {
visible: false,
};
},
methods: {
handleDialogVisible() {
this.visible = true;
}
}
}
</script>
ModalFirst.vue
<template>
<el-dialog
title="Tips"
:visible.sync="visible"
width="30%"
>
<span>This is a message</span>
<span slot="footer" class="dialog-footer">
<a>Hello</a>
</span>
</el-dialog>
</template>
<script>
export default {
props: [ 'visible' ]
}
</script>
And I can see a warning message on google chrome console after closing the dialog.
The warning message is below.
webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:620 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible"
found in
---> <ModalFirst> at components/ModalFirst.vue
<Pages/index.vue> at pages/index.vue
<Nuxt>
<Layouts/default.vue> at layouts/default.vue
<Root>
This is the screenshot of the warning message.
https://gyazo.com/83c5f7c5a8e4d6816c35b3116c80db0d
In vue , using directly to prop value is not allowed . Especially when your child component will update that prop value , in my option if prop will be use
for display only using directly is not a problem .
In your code , .sync will update syncronously update data so I recommend to create local data.
ModalFirst.vue
<el-dialog
title="Tips"
:visible.sync="localVisible"
width="30%"
>
<script>
export default {
props: [ 'visible' ],
data: function () {
return {
localVisible: this.visible // create local data using prop value
}
}
}
</script>
If you need the parent visible property to be updated, you can create your component to leverage v-model:
ModalFirst.vue
<el-dialog
title="Tips"
:visible.sync="localVisible"
width="30%"
>
<script>
export default {
props: [ 'value' ],
data() {
return {
localVisible: null
}
},
created() {
this.localVisible = this.value;
this.$watch('localVisible', (value, oldValue) => {
if(value !== oldValue) { // Optional
this.$emit('input', value); // Required
}
});
}
}
</script>
index.vue
<template>
<div>
<el-button type="text" #click="handleDialogVisible">click to open the Dialog</el-button>
<modal-first v-model="visible"></modal-first>
</div>
</template>
<script>
import ModalFirst from './../components/ModalFirst.vue'
export default {
components: {
'modal-first': ModalFirst
},
data() {
return {
visible: false,
};
},
methods: {
handleDialogVisible() {
this.visible = true;
}
}
}
</script>
v-model is basically a shorthand for :value and #input
https://v2.vuejs.org/v2/guide/forms.html#Basic-Usage
Side-note:
You can also import your component like so:
components: { ModalFirst },
as ModalFirst will be interpreted as modal-first as well by Vue.js
I am use component of the dialog window dialog.vue from vue-mdl package
<template>
<div class="mdl-dialog-container" v-show="show">
<div class="mdl-dialog">
<div class="mdl-dialog__title">{{title}}</div>
<div class="mdl-dialog__content">
<slot></slot>
</div>
<div class="mdl-dialog__actions" :class="actionsClasses">
<slot name="actions">
<mdl-button class="mdl-js-ripple-effect" #click.native.stop="close">Close</mdl-button>
</slot>
</div>
</div>
</div>
</template>
<script>
import mdlButton from './button.vue'
import createFocusTrap from 'focus-trap'
export default {
components: {
mdlButton
},
computed: {
actionsClasses () {
return {
'mdl-dialog__actions--full-width': this.fullWidth
}
}
},
data () {
return {
show: false
}
},
props: {
title: {
type: String
},
fullWidth: Boolean
},
mounted () {
this._focusTrap = createFocusTrap(this.$el)
},
methods: {
open () {
this.show = true
this.$nextTick(() => this._focusTrap.activate())
this.$emit('open')
},
close () {
this.show = false
this._focusTrap.deactivate()
this.$emit('close')
}
}
}
</script>
I want to bring a dialog window to the other component
<mdl-dialog></mdl-dialog>
<button class="mdl-button mdl-js-button mdl-button--raised">Click me</button>
I found no information on how to call a method of one component within the other. All examples are mainly used props. Tell me how to do it?
How can I call a method open() in <mdl-dialog></mdl-dialog>?
Since they're not parent child you'd want to use an event bus. Since you're using .vue files you can create a file called bus.js like
import Vue from 'vue'
export default new Vue()
Then, import that wherever you need to emit and listen for centralized events. Here's a quick example:
// SomeComponent.vue
import bus from './bus.js'
export default {
methods: {
log (msg) {
console.log(msg)
}
},
created () {
bus.$on('someEvent', this.log)
}
}
Then in another component you can do like...
// AnotherComponent.vue
import bus from './bus.js'
export default {
methods: {
emitClick (msg) {
bus.$emit('Hello from AnotherComponent.vue')
},
},
}
You can read up a bit more about it here: https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
You can create below helper method in methods in your parent component:
getChild(name) {
for(let child of this.$children) if (child.$options.name==name) return child;
},
And call child component method in this way:
this.getChild('mdl-dialog').open();
I don't test it for Vue>=2.0