Vuetify: Navigation drawer: communicate v-model changes to parent co - vuejs2

I have created a Sidebar component using Vuetify's navigation drawer. The code looks something like this:
<template>
<v-navigation-drawer persistent clipped v-model="isVisible" fixed app>
<!-- content of the sidebar goes here -->
</v-navigation-drawer>
</template>
<script>
export default {
name: 'Sidebar',
props: {
visible: Boolean,
},
data() {
return {
isVisible: this.visible,
};
},
}
</script>
Please note that I am duplicating the visible prop with the isVisible data. I tried using the prop directly in the v-model but every time the sidebar closed, I would get a warning in the console about changing props directly, as they would be overwritten when the parent re-renders.
In the parent view, I have a button on the toolbar that is supposed to change icon depending on the visibility of the toolbar.
<template>
<v-container fluid>
<sidebar :visible="sidebarVisible"/>
<v-toolbar app :clipped-left="true">
<v-btn icon #click.stop="sidebarVisible = !sidebarVisible">
<v-icon v-html="sidebarVisible ? 'chevron_right' : 'chevron_left'"/>
</v-btn>
</v-toolbar>
<v-content>
<router-view/>
</v-content>
<v-footer :fixed="fixed" app>
<span>© 2017</span>
</v-footer>
</v-container>
</template>
<script>
import Sidebar from '#/components/Sidebar.vue';
export default {
name: 'MainView',
data() {
return {
sidebarVisible: false,
fixed: false,
title: 'Title',
};
},
components: {
Sidebar,
},
};
</script>
The problem I have is that if I close the sidebar by clicking outside of it, the icon of the button on the toolbar does not change to chevron-left. Moreover, in order to bring the sidebar back, I need to click on the button twice.
Clearly this is because the sidebarVisible data in the main view is not updated when the sidebar closes. How do I make sure that sidebarVisible is updated when the sidebar closes?

I am use next construction...
in my component
<template>
<v-navigation-drawer v-model="localDrawer"></v-navigation-drawer>
</template>
...
<script>
export default {
props: { value: { type: Boolean } },
data: () => ({
localDrawer: this.value
}),
watch: {
value: function() {
this.localDrawer = this.value
},
localDrawer: function() {
this.$emit('input', this.localDrawer)
}
}
}
</script>
in parent layer
<app-drawer v-model="drawer"></app-drawer>
it's work for me

Use v-bind:value or :value to bind the drawer value from props.
Child component:
<template>
<v-navigation-drawer v-bind:value="drawer"></v-navigation-drawer>
</template>
<script>
export default {
props : ['drawer']
}
</script>
Parent component:
<template>
<app-side-bar :drawer="drawer"/>
<v-app-bar app clipped-left>
<v-app-bar-nav-icon #click.stop="drawer = !drawer"></v-app-bar-nav-icon>
</v-app-bar>
</template>

Vuetify navigation drawer issue fix:
Reset your browser window to default 100%
Here is the code,
Template:
<nav>
<v-toolbar flat app>
<v-toolbar-side-icon class="grey--text" #click="toggle"></v-toolbar-side-icon>
<v-toolbar-title class="text-uppercase grey--text">
<span class="font-weight-light">Repo</span>
<span>hub</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn flat color="grey">
<span>Sign Out</span>
<v-icon right>exit_to_app</v-icon>
</v-btn>
</v-toolbar>
<v-navigation-drawer app v-model="drawer" class="indigo">
<p>test</p>
</v-navigation-drawer>
</nav>
Script:
export default {
data() {
return {
drawer: false
};
},
methods:{
toggle(){
this.drawer = !this.drawer;
}
}
};

Related

Update property value of child component from parent component

I have a parent component that has a button element. If user clicks that button, a dialog component should appear. The child component is the dialog component. However, the dialog won't open.
This is how I propagate props to my child component:
Parent component (template):
<v-btn
color="primary"
elevation="5"
#click="openCreateDialog = true"
small
>
<v-icon left>
mdi-plus
</v-icon>
Create
</v-btn>
<create-reservation-dialog :open="openCreateDialog" />
Child component (template):
<v-dialog v-model="openDialog" width="300">
<span>Opened</span>
</v-dialog>
Child component (JS):
export default Vue.extend({
name: "CreateReservationDialog",
props: {
open: {
type: Boolean,
required: true,
},
},
data() {
return {
openDialog: this.open,
}
},
});
Any ideas how can I fix this? Should I bind some method that will update openDialog variable in child? If so how?
Thank you in advance.
on clicking you are just updating the property value but it will not reload the dialog component. You can add v-if="openCreateDialog" in create-reservation-dialog to render it dynamically.
Live Demo (Please notice here, how mounted hook called in the child component) :
Vue.component('custom-dialog', {
props: ['open'],
template: '<p>{{ open }}</p>',
mounted() {
console.log('popup called');
}
});
var app = new Vue({
el: '#app',
data: {
openCreateDialog: false
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="openCreateDialog = true">Open Dialog</button>
<custom-dialog v-if="openCreateDialog" :open="openCreateDialog">
</custom-dialog>
</div>

Close a Vuetify v-dialog in a custom component when using v-slot:activator

I am trying to create a custom component that wraps a Vuetify v-dialog.
I am having trouble with closing the dialog using a button inside the dialog.
I've tried many things, like #emit('input', false), this.value = false or using a local data value instead of value, but nothing seems to work.
My dialog looks like this (simplified):
// file: DeleteDialog.vue
<template>
<v-dialog :value="value" #input="$emit('input', $event)" width="550">
<template v-slot:activator="{ on, attrs }">
<slot
name="activator"
v-bind:on="on"
v-bind:attrs="attrs"></slot>
</template>
<v-btn #click="closeDialog">
Close
</v-btn>
</v-dialog>
</template>
<script>
export default {
props: {
value: Boolean,
},
methods: {
closeDialog() {
this.$emit('input', false);
},
},
};
</script>
When using the dialog like this, how do I get the Close-button to work?
<DeleteDialog>
<template v-slot:activator="{on, attrs">
<v-btn v-on="on" v-bind="attrs">
Show dialog
</v-btn>
</template>
Are you sure you want to delete this user?
</DeleteDialog>
The prop you pass value is responsible for showing or hiding your v-dialog inside DeleteDialog.vue.
So when clicking the button close, we will emit an event close that will maake the parent of DeleteDialog change the prop value it passed to it as false.
// in your parent component
<DeleteDialog :value="show_dialog" #close="show_dialog = false>
in your deleteDialog
//in delete dialog
<template>
<v-dialog :value="value" width="550">
<v-btn #click="closeDialog">
Close
</v-btn>
</v-dialog>
</template>
<script>
export default {
props: {
value: Boolean,
},
methods: {
closeDialog() {
this.$emit('close');
},
},
};
</script>
I think that is more suitable way of achieving what you want

Pass click function with input v-model params

I have a btnSmall component with this props and click
<v-btn
class="white--text btn_main_product"
color="red darken-1"
#click="click"
>
<slot/>
</v-btn>
props: {
click: Function
}
I use this component like this in another component.
<template>
<v-card
class="pa-4"
>
<v-text-field
v-model="lname"
></v-text-field>
<v-btn :click="clickAccept(lname)">accept/v-btn>
</v-card>
</template>
<script>
import BtnSmall from "../buttons/btnSmall";
export default {
name: "editName",
components: {BtnSmall},
data() {
return {
lname: ''
}
},
props: ['clickAccept']
}
</script>
and use this component in the page.
after use <edit-name :click-accept="nameClickAccept"/> whit this method.
nameClickAccept(fname, lname) {
console.log(lname)
}
my method run with change text-field i cant use click.
i need run method with click on the btn.
The vue-approach would be to emit an event in the btnSmall component and react on that event in the parent component.. This: https://v3.vuejs.org/guide/component-custom-events.html#event-names shows you how you can do it.

Vue Composition API createComponent child components

I'm simply trying to use a child component in a Nuxt/Composition API/TypeScript project using Vuetify. I've tried including the component before the setup method, but when I view the source I only see <!----> where the child component data should be.
This is the layouts/default.vue layout that should be displaying the "nested" component.
<template>
<v-app>
<v-app-bar clipped-left app color="indigo darken-2" />
<v-content>
<nuxt />
</v-content>
<v-bottom-navigation
v-model="bottomNav"
color="indigo darken-2"
absolute
class="hidden-sm-and-up"
>
<v-btn
value="search"
#click="dialog=true"
>
<span>Search</span>
<v-icon>mdi-magnify</v-icon>
</v-btn>
</v-bottom-navigation>
<v-dialog v-model="dialog">
<ListFilter
#closeDialog="closeDialog"
/>
</v-dialog>
</v-app>
</template>
<script lang="ts">
import { createComponent, ref } from '#vue/composition-api'
import ListFilter from '~/components/ListFilter.vue'
export default createComponent({
components: { ListFilter },
setup() {
const bottomNav = ref<string>()
const dialog = ref<boolean>(false)
const closeDialog = () => {
dialog.value = false
bottomNav.value = ''
}
return {
bottomNav,
dialog,
closeDialog
}
}
})
</script>
Here is the sample contents of the components/ListFilter.vue component to be nested, the contents of which matter little.
<template>
<v-card>
<v-card-title>
Search
</v-card-title>
<v-card-text>
Test Text
</v-card-text>
<v-card-actions>
<v-btn
#click="closeDialog"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</template>
<script lang="ts">
import { createComponent } from '#vue/composition-api'
export default createComponent({
setup(_, { emit }) {
const closeDialog = () => {
emit('closeDialog')
}
return closeDialog
}
})
</script>
Problem is return closeDialog in setup function of ListFilter component. If you return a function from setup(), Vue expects it is a render function. Try return { closeDialog } instead...

Vuetify text displaying on the right side of the v-navigation-drawer

I am a new programmer and trying to make a non-toggling side bar, like admin page.
The router-view doesnt display on the right side of the page.
I know that I don't have a href link for each of the navigation list, where and how can I do that?
also, is the Apps causing conflict with the sellingSummary page?
and am I inserting the router-view in the wrong place?
I need help, thank you !!!!
image
<template>
<div>
<v-app id="inspire">
<v-navigation-drawer
stateless
value="true"
>
<v-list>
<v-list-group
no-action
prepend-icon="account_circle"
value="true"
>
<v-list-tile slot="activator">
<v-list-tile-title>admins</v-list-tile-title>
</v-list-tile>
<v-list-tile
v-for="(admin, i) in admins"
:key="i"
#click=""
>
<v-list-tile-title v-text="admin[0]"></v-list-tile-title>
<v-list-tile-action>
<v-icon v-text="admin[1]"></v-icon>
</v-list-tile-action>
</v-list-tile>
</v-list-group>
</v-list>
</v-navigation-drawer>
<div>
<router-view></router-view>
</div>
</v-app>
</div>
</template>
<script>
export default {
data() {
return {
admins: [
['Management', 'people_outline'],
['Settings', 'settings']
]
}
}
}
</script>
./router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import AppS from '#/components/SellingPage/AppS'
import sellingSummary from '#/components/SellingPage/subPage/sellingSummary'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/SellingPage',
name: 'AppS',
component: AppS
},
{
path: '/SellingPage',
name: 'sellingSummary',
component: sellingSummary
}
]
})
./components/SellingPage/subPage/sellingSummary
<template>
<div>
<p>Hi, first</p>
</div>
</template>
./components/SellingPage/AppS
<template>
<div>
<app-header></app-header>
<v-card>
<app-selling>
</app-selling>
</v-card>
</div>
</template>
<script>
import Header from '../Header.vue';
import Selling from './Selling.vue';
export default {
components: {
'app-header': Header,
'app-selling': Selling
}
}
</script>
<v-navigation-drawer
disable-route-watcher
enable-resize-watcher
:clipped="clipped"
v-model="sideNav"
dark
app
>
try this
For starters, you can't have two routes in your router configured for different components. The Router will only use the first entry.
In your component Vue file add the following property in the data property
drawer: true,
Example:
export default {
data: () => ({
drawer: true
})
}
The Navigation drawer has an item data property which can be used to loop through existing menu items.
In some of the properties, you can add a path attribute directing to an existing path on the router when that menu item is pressed.
Here is an example:
https://codepen.io/wernerm/pen/rzqqbR