I'm using Vuetify to create a couple of components in a dashboard (example code).
As you can see, I have a toolbar component:
Vue.component('toolbar', {
template: '
<v-toolbar>
<v-toolbar-side-icon #click="$emit('toggle')"></v-toolbar-side-icon>
<v-toolbar-title>Application</v-toolbar-title>
</v-toolbar>'
});
I want to be able to click this toolbar to close the drawer component, so I emit an event for it: #click="$emit('toggle')"
Then, the drawer component listens with #toggle for that event to swap the boolean value of the v-model=drawer:
Vue.component('drawer', {
template: '
<v-navigation-drawer v-model="drawer" #toggle="drawer=!drawer" app>
...
</v-navigation-drawer>',
data() {
return { drawer: true }
}
});
However, clicking on toggle doesn't change value of the v-model. In order for the Vuetify component to work I need to be able to change the v-model value, I don't think I can make it work with computed properties, so I guess this rules out using Vuex.
What would be the simplest way to handle the communication between these components?
There are couple of changes that needs to be done:
add the property drawer: true on the root instance where the drawer component is rendered as pass it as props to the drawer component
new Vue({
el: '#app',
data: {
drawer: true
}
})
template
<drawer :drawer="drawer"></drawer> // pass drawer as props
Receive the drawer prop in the drawer component and set it to the v-model of <v-navigation-drawer>
Vue.component('drawer', {
template: `
<v-navigation-drawer clipped fixed app v-model="drawer">
...
</v-navigation-drawer>`,
props: {
drawer: Boolean,
source: String
}
});
you are emitting an event from the toolbar component. So you should add the event listener on the toolbar component , not the drawer component.
<toolbar #toggle="drawer = !drawer"></toolbar>
Here is the updated codepen: https://codepen.io/anon/pen/aYVYzQ?editors=1010#anon-login
Related
I'm trying to make a plugin just to show a modal div with some custom personalization, so i thought of a plugin with a custom component. The idea is to call the plugin that should show the modal box (the component) but i cannot understand how to pass plugin methods props to the component.
I have this plugin
import Modal from './components/Modal'
const ModalComponent = {
install (Vue) {
Vue.component('modal', Modal)
Vue.mixin({
data () {
return {
status: null,
modalMessage: null,
show: false
}
},
component: {
Modal
},
methods: {
setMessage (status, modalMessage) {
console.log(status)
console.log(modalMessage)
this.status = status
this.modalMessage = modalMessage
this.show = true
}
}
})
}
}
export default ModalComponent
i can call the plugin in my other components in this way this.setMessage(res.status, res.message)
The component is this
<template>
<b-modal
v-model="show"
centered
cancel-disabled
:title="title"
:header-bg-variant="header_bg"
><p class="my-4">{{modalMessage}}</p>
</b-modal>
</template>
<script>
export default {
name: "modal",
data() {
return {
show: false,
title: null,
modalMessage: null,
header_bg: null
}
},
props: {
show: Boolean,
title: String,
modalMessage: String,
header_bg: String
}
}
</script>
How can i tell the component to show using props from the plugin? I miss the passage to send props to it when using the plugin methods
The problem seems to be that your plugin doesn't generate any element. You're creating a method on every component using the mixin, but this will assume that every one of your components' templates has a <Modal :status="status" :modalMessage="modalMessage" :show="true" />
I would suggest a different approach...
in your main template, add the instance of the modal. Then you can use a global state of some sort. I prefer using vuex for this, since you can use the vuex state to mange the props, and use a globally available action (commit) with the desired payload. Alternatively, you can use the vue internals to modify state, but if you're already using vuex, this likely the easiest option.
I've been trying to solve this mystery for a week now.
I'm trying to set up a sidebar with Vuetify in my Nuxt/Vue website where people click on the hamburger menu and the sidebar opens up. To do this I set up the hamburger menu to run a toggle method.
<v-app-bar-nav-icon #click="toggleSidebar"></v-app-bar-nav-icon>
......
<script>
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations({
toggleSidebar: 'appSidebar/toggleSidebar'
})
}
}
</script>
The method then update vuex state
export const state = () => ({
sidebarOpen: false
})
export const mutations = {
toggleSidebar(state) {
state.sidebarOpen = !state.sidebarOpen;
},
}
This works fine. The bar opens and close when I click on the hamburger menu. However, for some reason, which I'm glad it does, the sidebar closes when I click outside the sidebar (if anyone could explain how this works too i'd be appreciated). When this happens, the state doesn't update and the next time i want to open i need to click on the menu twice to get it back to close then open again.
This is how i set up the sidebar
<v-app>
<v-navigation-drawer app temporary clipped v-model="status" color="blue lighten-3" dark>
<script>
export default {
computed: {
status (){
return this.$store.state.appSidebar.sidebarOpen
}
}
}
</script>
Thank you for your help!
The drawer closes when you click outside because of the temporary prop. According to your code, your state changes only on clicking the hamburger button. But internally vuetify uses the temporary property. You can either do without your vuex code or without the temporary prop.
i have a bootstrap-vue input on a modal
<b-form-input id="inputText1" ref="inputText1" v-model="inputText" autofocus></b-form-input>
the modal is a bootstrap-vue modal and the show/hide is controlled by a v-if directive.
When the modal is opened the input has focus. If i close the modal the input doesn't have focus anymore.
I have tried to set the autofocus property each time the modal is mounted but it still doesn't focus. I have tried using $nextTick also.
I recomend to you use v-model with vue bootstrap modal
template
<template>
<div>
<b-button #click="showModal= !showModal">Open your bmodal</b-button>
<b-modal v-model="showModal">Yor modal is active!</b-modal>
<b-form-input id="inputText1" ref="inputText1" v-model="inputText" autofocus></b-form-input>
</div>
</template>
vue code
new Vue({
el: '#app',
data() {
return {
showModal: false
}
},
watch:{
showModal:function(value){
// set the focus when the modal opened/closed
this.$refs.inputText1.focus();
}
},
mounted(){
// set the focus when the component opened
this.$refs.inputText1.focus();
},
methods: {
}
});
The solution is to listen to the modal event shown:
<b-modal #shown="$refs.inputText1.focus()"></b-modal>
Unlike other elements where you can use created or mounted to reliably focus, modals require a different approach because they get inserted into the body and the hooks don't fire as others do.
I'm using Vuetify to create my social media website. The problem that I'm facing now is that I want to use dark attribute, so user can switch between normal and dark theme. The thing is that I can't use any of Vue's conditional rendering methods, as dark is not an attribute that you can bind. Below is the part of code that you use to apply dark theme:
<v-app dark>
Yes dark or light are not attributes, they are props which can take values, in this case true or false
You can find this in vuetify's documentations.
Props are the properties which are used for communication with child components. And are prefixed with : in order to distinguish it with normal properties.
Coming to the solution.
<v-app :dark="true">
or
<v-app :dark="false">
You can replace true or false with any reactive data options or computed properties to make the theme change programmatically.
To be able to persist theme with localstorage
Inside plugins/vuetify.js or plugins/vuetify.ts add this:
export default new Vuetify({
theme: {
//
dark: localStorage.getItem('theme') === 'dark',
//
}
})
then on the button you want to switch theme apply the following function:
switchTheme() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark;
localStorage.setItem('theme', this.$vuetify.theme.dark ? 'dark' : 'light');
}
As documentation says you can just update variable this.$vuetify.theme.dark:
You can manually turn dark on and off by changing this.$vuetify.theme.dark to true or false.
If a component is used as the App:
<template>
<v-app :dark="isDark"></v-app>
</template>
<script>
export default {
data () {
return {
isDark: false,
}
},
}
</script>
It can also be called from the app instance
thru its instantiated object defined property
app.__vue__.isDark = isDark
when being instantiated like :
const app = new Vue(
{
data: () => ({
isDark: false
}),
}
)
So it can be called from other component
I have added a component declaration to the default main.js file which is generated during the Webpack project creation process as
import Modal from '#/components/Modal'
Vue.component('modal', Modal)
And in the App.vue, I have
<modal v-show="showModal"></modal>
<button id="show-modal" v-on:click="showModal = true">Click to have a modal</button>
And they work fine. Now, I need to setup a "props down, events up" communication channel between the parent and a child. To do so, I need to add a property, called 'isActive', the Modal component so that the root component can send a message to the child component, that is
<modal isActive="showModal"></modal>
<button id="show-modal" v-on:click="showModal = true">Click to have a modal</button>
I guess the component declaration should be something like:
Vue.component('modal', {
props: ['isActive'],
Modal
})
It doesn't work, however, due to
Failed to mount component: template or render function not defined.
I have tried different variants without a luck.
My second question is that how a child event changes its parent data. For example, in the child component
<button class="modal-close is-large" v-on:click="closeModal"></button>
the closeModal event is handled in the following javacript code in the child component.
export default {
method: {
closeModal: function(event) {
...
}
}
}
How can I set its parent data showModal to false?
Update:
The code segment of Modal:
<template>
<div class="signin">
<div class="modal" v-bind:class="{ 'is-active': isActive }">
...
</div>
<button class="modal-close is-large" v-on:click="isActive = false"></button>
</div>
</div>
</template>
<script>
import axios from 'axios'
import _ from 'lodash'
import Notification from '#/components/Notification'
import { required, email } from 'vuelidate/lib/validators'
export default {
name: 'signin',
components: {
Notification
},
data: () => ({
isActive: true,
email: '',
...
}),
...
}
</script>
Bulma is used for styling. And the isActive is defined in the Modal. I think it needs to be changed to achieve "props down".
As it looks, your file /components/Modal contains a full definition of a component: the template, and the script parts for it. So you can just bind the component to the tag-name you want to use in your markup:
import Modal from '#/components/Modal'
Vue.component('modal', Modal)
This is basically what you had in the beginning. To pass properties to this component, add the props-line directly to your component, that is into /components/Modal:
...
export default {
name: 'signin',
components: {
Notification
},
props: ['isActive'],
data: () => ({
...
As for the second question, how to communicate back to the parent, have a look at Vue's Custom Events. Basically, your Modal component could issue a "close"-event like this:
methods: {
closeModal: function(event) {
this.$emit('modalClose')
}
}
and when you use the component, you could listen to it like this:
<modal v-bind:isActive="showModal" v-on:modalClose="showModal = false"></modal>
Note that you should use v-bind for providing the value to isActive. If you don't use v-bind, the value is just passed once when the component is created. This means, the component would never see a change to this prop when it is changed by the parent. By using v-bind, changes by the parent to this attribute are pushed down to the child-component, so the Modal actually sees the updated value and can react to it.