Switch Vuetify navigation drawer to mini and then temporary - vue.js

I have a project in Vue.js and I am using Vuetify. I have a toolbar and navigation drawer. What I would like is when on desktop the drawer is open. If the user clicks the side-icon the drawer switches to mini.
If on md the drawer switches to mini. if the user clicks the side-icon the mini switches back to drawer
If on sm or lower the navigation drawer switches to temporary
I have most of the pieces but I am getting an error when I click the side-icon. Computed property 'mini' was assigned to but it has no setter.
Here is my code:
<v-toolbar
:clipped-left="$vuetify.breakpoint.mdAndUp"
:app="$vuetify.breakpoint.mdAndUp"
:fixed="$vuetify.breakpoint.mdAndUp"
flat
fixed
:scroll-toolbar-off-screen="$vuetify.breakpoint.smAndDown"
:scroll-threshold="50">
<v-toolbar-side-icon #click.stop="mini = !mini">
</v-toolbar-side-icon>
<v-toolbar-title class="text-uppercase">
<span class="font-weight-light">LOGO</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items class="">
<v-btn icon v-for="item in menu" :key="item.icon">
<v-icon>{{item.icon}}</v-icon>
</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-navigation-drawer
clipped
:mini-variant="mini"
v-model="drawer"
:permanent="$vuetify.breakpoint.mdOnly"
:temporary="$vuetify.breakpoint.smAndDown"
app
hide-overlay>
<v-list dense>
<v-list-tile
v-for="(item, index) in items"
:key="index"
>
<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>
I have created a codepen with what I have so far:
https://codepen.io/jsd219/pen/gJJMPQ

You're trying to assign a computed property to itself:
#click.stop="mini != mini"
You really don't want to do that. To find out why, you want to read on JS setters and getters.
If you want mini to be the computed which determines if your <navigation-drawer> is minified or not, use two separate placeholders for your info:
one for whether the menu is forcefully opened (call it menuOpen), initially defined in data(), as false and then overwritten by your #click.stop="menuOpen != menuOpen"
and one coming from $vuetify.breakpoint.mdAndUp. Call it mdAndUp.
So your mini becomes:
mini() {
return !(this.mdAndUp|| this.menuOpen);
}
See it here.

The problem is that you are trying to modify the computed property mini.
To mutate a computed property, you would need to provide a computed setter:
computed: {
mini: {
get() {
// get logic
},
set(value) {
// set logic
}
}
}
In your case, your computed property mini returns true or false if based on $vuetify.breakpoint.mdAndDown. You would need to include a new variable, something like overwriteBreakpoint and use that in your setter.
data() => ({
overwriteBreakpoint: true
}),
computed: {
mini: {
get() {
return this.$vuetify.breakpoint.mdAndDown || this.overwriteBreakpoint;
},
set(value) {
this.overwriteBreakpoint = value;
}
}
}
Here's an example: https://codepen.io/dormenog/pen/MddbMY?editors=1011
Update:
To make this work on multiple screen sizes you'll need to come up with defined rules on when each prop of your nav bar should be true or false. This will become very messy, very quickly, and the benefit of the keeping track of the state is not really valuable because the screens will not change size in real time on the user's device.
I would advice separating the contents of your navbar into a component and wrap it with multiple <v-navigation-drawer /> that will only be rendered by vue if the screen size is correct. This can be achieved using v-if and v-else.

Related

Access to a slot function inside a child component in VusJS

I'm trying to use tiptap. Actually it works, but what I'm trying to do is to access the "isActive" slot from outside the editor component, and I don't know how to do it.
Here is a codesandbox example: https://codesandbox.io/s/v07xnxo807?file=/src/App.vue
You see the Editor component is called from the App.vue. The buttons in the Editor component are activated depending on the "isActive" slot functions.
What I would like is to access this slot to get for example the value of isActive.bold() from the App.vue, in order to update the model of a "multiple button" you can find on Vuetify: https://vuetifyjs.com/fr-FR/components/button-groups/
Here is for example what I could have:
<editor-menu-bar :editor="editor" v-slot="{ commands, isActive }">
<v-btn-toggle
v-model="toggle_multiple"
dense
background-color="primary"
dark
multiple
class="my-2"
>
<v-btn :color="isActive.bold()?'red':'green'" #click="commands.bold">
<v-icon>mdi-format-bold</v-icon>
</v-btn>
<v-btn #click="commands.italic">
<v-icon>mdi-format-italic</v-icon>
</v-btn>
<v-btn #click="commands.strike">
<v-icon>mdi-format-strikethrough</v-icon>
</v-btn>
<v-btn #click="commands.underline">
<v-icon>mdi-format-underline</v-icon>
</v-btn>
<v-btn #click="commands.blockquote">
<v-icon>mdi-format-quote-open</v-icon>
</v-btn>
</v-btn-toggle>
</editor-menu-bar>
And the toggle_multiple would be computed depending on the different "isActive" function values.
I already tried:
computed: {
toggle_multiple: function () {
let t = []
if (editor) {console.log("Bold: "+editor.isActive.bold())}
return t
}
},
But I receive this error: error 'editor' is not defined
I'm open to any suggestion.
Thanks in advance.
Property isActive is stored in the tiptap instance (in your case it's this.editor):
In HTML:
<div>{{editor.isActive.bold()}}</div>
In JS:
<div>{{toggle_multiple}}</div>
computed: {
toggle_multiple () {
// Keep in mind, other properties like ".isActive.heading()" will be undefined
// until you import the extension for it.
// So the function "heading" below exists only if you're using that extension
// console.log(this.editor.isActive.heading({ level: 2 })
return this.editor.isActive.bold()
}
}

Can't Close Vuetify Navigation Drawer with Escape Key

I am trying to find a way to close the vuetify navigation drawer component by clicking on the escape key. To do so, I tried using a key modifier as follows:
<v-navigation-drawer
v-model="drawer"
color="dark"
app
#keydown.esc="drawer = false"
>
Theoretically, I would expect this to work, but it does not. Nothing happens when I click on the escape key. Any idea how to get this to work?
For what it is worth, here is an outline of the code for the entire app-bar/navigation bar section:
<template>
<v-app>
<v-app-bar color="primary" app dark flat>
...
<v-app-bar-nav-icon
#click="drawer = !drawer"
></v-app-bar-nav-icon>
</v-app-bar>
<v-navigation-drawer
v-model="drawer"
color="dark"
app
#keydown.esc="drawer = false"
>
...
</v-navigation-drawer>
</v-app>
</template>
You cannot do that with a drawer, it's not an input element.
Set up a global listener instead:
mounted () {
window.addEventListener('keydown', this.keyDownHandler)
},
destroyed () {
window.removeEventListener('keydown', this.keyDownHandler)
},
methods: {
keyDownHandler (event) {
if (event.code === 'Escape') {
this.drawer = false
}
}
}

Vue js - How to use props in data and methods

I am new in vue js , I am passing data from parent component to child one using props and I can use it in child normally but I can't use it in data of child component
parent
<template>
<div>
<show-question :qdata="question" v-if="question"></show-question>
<h1 v-if="!question"> some error </h1>
</div>
</template>
<script>
import ShowQuestion from './ShowQuestion';
export default {
created(){
axios.get(`/api/question/${this.$route.params.slug}`)
.then(res => {
this.question = res.data.data
})
},
data(){
return {
question : {},
}
},
components:{
ShowQuestion,
},
}
</script>
child
<template>
<v-container>
<v-card>
<div>
<v-card-title class="blue--text"
>{{ data.title }}
<v-spacer></v-spacer>
<v-btn color="teal white--text">5 Replies</v-btn>
</v-card-title>
<v-card-subtitle
> {{data.uid}} {{ data.user }} said {{ data.created_at }}</v-card-subtitle
>
</div>
<v-card-text>{{ data.body }}</v-card-text>
<v-card-actions v-if="own">
<v-btn icon text>
<v-icon color="orange">create</v-icon>
</v-btn>
<v-btn icon text>
<v-icon color="red">delete</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-container>
</template>
<script>
export default {
props: ['qdata'],
data(){
return {
own: User.own(this.qdata.uid),
};
},
};
</script>
this.qdata.uid is always be undefined in console, although it supposed it have values and I can saw it from child template
enter image description here
Your show-questioncomponent is mounted early on because v-if="question" is true. When show-question is mounted, your api call hasn't had a chance to finish, so question is the same as the initial value {}, which is why uid is undefined.
I would change question : {} to question: null, then the child component will only be mounted when there's a question (after the api call).
This is simply because if you check the truthy of an object it will always return true even if the object is empty, which results in the component being rendered before the API call has finished.
Instead, you can check if it's empty or not by converting it to an array and check it's length value, i.e. Object.entries(question).length or simply use the lodash helper method: _isEmpty(question).
Also a quick side note: it's cleaner to use v-else after v-if when you want to render something or the other instead of explicitly negating the value in another v-if, though they're required to be direct siblings.

Vuetify App went blank after upgrade to vuetify 2.0 [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
I am currently using vuetify 1.5.16, and when I use npm to upgrade to 2.0.
The application won't work anymore.
You could find the code from below, it's a basic drawer content toolbar layout which now couldn't be rendered.
I've looked into the official document, I didn't find any upgrade guide or deprecated warning.
new Vue({
el: '#app',
data: () => ({
drawer: {
// sets the open status of the drawer
open: true,
// sets if the drawer is shown above (false) or below (true) the toolbar
clipped: false,
// sets if the drawer is CSS positioned as 'fixed'
fixed: false,
// sets if the drawer remains visible all the time (true) or not (false)
permanent: true,
// sets the drawer to the mini variant, showing only icons, of itself (true)
// or showing the full drawer (false)
mini: true
},
toolbar: {
//
fixed: true,
// sets if the toolbar contents is leaving space for drawer (false) or not (true)
clippedLeft: false
},
footer: {
// sets the CSS position of the footer
fixed: true,
// sets if the footer is full width (true) or gives space to the drawer (false)
clippedLeft: true
}
}),
props: {
source: String
},
methods: {
// changes the drawer to permanent
makeDrawerPermanent () {
this.drawer.permanent = true
// set the clipped state of the drawer and toolbar
this.drawer.clipped = false
this.toolbar.clippedLeft = false
},
// toggles the drawer variant (mini/full)
toggleMiniDrawer () {
this.drawer.mini = !this.drawer.mini
},
// toggles the drawer type (permanent vs temporary) or shows/hides the drawer
toggleDrawer () {
if (this.drawer.permanent) {
this.drawer.permanent = !this.drawer.permanent
// set the clipped state of the drawer and toolbar
this.drawer.clipped = true
this.toolbar.clippedLeft = true
} else {
// normal drawer
this.drawer.open = !this.drawer.open
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.18/vue.js"></script>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire" dark>
<v-navigation-drawer
:clipped="drawer.clipped"
:fixed="drawer.fixed"
:permanent="drawer.permanent"
:mini-variant="drawer.mini"
v-model="drawer.open"
app
>
<v-list>
<v-list-tile
v-if="!drawer.permanent"
#click="makeDrawerPermanent">
<v-list-tile-action><v-icon>chevron_right</v-icon></v-list-tile-action>
<v-list-tile-content><v-list-tile-title>Static Drawer</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile #click="toggleMiniDrawer">
<v-list-tile-action><v-icon>aspect_ratio</v-icon></v-list-tile-action>
<v-list-tile-content><v-list-tile-title>Mini Drawer</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-divider></v-divider>
<v-list-tile #click="">
<v-list-tile-action><v-icon>dashboard</v-icon></v-list-tile-action>
<v-list-tile-content><v-list-tile-title>Dashboard</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>
<v-toolbar
app
:fixed="toolbar.fixed"
:clipped-left="toolbar.clippedLeft"
>
<v-toolbar-side-icon
#click.stop="toggleDrawer"
></v-toolbar-side-icon>
<v-toolbar-title>Vuetify Drawer Example</v-toolbar-title>
</v-toolbar>
<v-content>
<v-container fluid fill-height>
<v-layout justify-center align-center>
<v-flex shrink>
<h2>Vuetify Drawer example</h2>
<p>Showing how to set the navigation drawer into different positions and styles</p>
<p>This took me a hour to comprehend properly, so this pen may save others some time</p>
<p>As always, if you can do it better, then please fork it and let me know</p>
<v-tooltip right>
<v-btn
icon
large
:href="source"
target="_blank"
slot="activator"
>
<v-icon large>code</v-icon>
</v-btn>
<span>Source</span>
</v-tooltip>
</v-flex>
</v-layout>
</v-container>
</v-content>
<v-footer app :fixed="footer.fixed" :clipped-left="footer.clippedLeft">
<span class="caption mx-3">© 2018, MIT LICENSE - Free to use and learn from</span>
</v-footer>
</v-app>
</div>
the upgrade guide is available in the gitlab release notes
https://github.com/vuetifyjs/vuetify/releases?after=v2.0.3#user-content-upgrade-guide
looks like you're just missing the new vuetify instantiation
new Vue({
el: '#app',
vuetify: new Vuetify(),
//...
})

Vue Change icon to click on the button

how can I change the icon when I click on the button in vue.
Here is a portion of the code:
<v-btn flat icon color="white">
<v-icon>star_border</v-icon>
</v-btn>
Thanks
<v-btn #click="show = !show" icon>
<v-icon>{{ !show ? 'mdi-eye' : 'mdi-close' }}</v-icon>
</v-btn>
add to component
{
data(){
return {
show:false
}
}
}
Hi Enzo and congrats on starting your VueJS project.
I would recommend you looking at VueJS documentation about Data and Methods, to give you some start. https://v2.vuejs.org/v2/guide/instance.html#Data-and-Methods
In short Data is where you keep your reactive properties and Methods store your functionality.
Right now the name of the icon is hard-coded. What you want to do is to make it reactive. So to change the icon;.
You need to bind the name of the icon to a property in your data.
Define a method that changes the value of that property.
Make an on-click event to invoke a function.
Something like this:
new Vue({
el: '#app',
data() {
return {
myIcon: 'star_border'
}
},
methods: {
changeIcon() {
this.myIcon = 'home'
}
}
})
Here I've defined a property called myIcon, which is 'star-border' at first. I've also created a method that gets invoked on the click-event of this button. This method changes the value of the myIcon property to 'home'.
You can see a working demo here: https://codepen.io/bergur/pen/MLMxzY