I need to make right aligned v-menu with "attach" option.
Template:
<div id="app">
<v-app id="inspire">
<h1>VMenu bug with "right" option</h1>
<div class="place"></div>
<div class="text-center">
<v-btn
color="primary"
dark
#click="show = !show"
>
Dropdown
</v-btn>
</div>
<v-menu attach=".place" v-model="show" :right="true">
<v-list>
<v-list-item
v-for="(item, index) in items"
:key="index"
#click=""
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app>
</div>
JS:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
show: false,
items: [
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me 2' },
],
}),
})
I expect right aligned menu in ".place" element. But the menu is left aligned. Also top border of menu is under the ".place" element. It is strange. How can I fix it?
Demo
It is possible to align the content of v-menu to right border of the page
Here is the working codepen: https://codepen.io/chansv/pen/OJyjWmX
<div id="app">
<v-app id="inspire">
<h1>VMenu bug with "right" option</h1>
<div>
<div id="attachMenu" style="float: right;position: relative;width: 134px;left: 27px;"></div>
</div>
<div class="text-center">
<v-btn
color="primary"
dark
#click="show = !show"
>
Dropdown
</v-btn>
</div>
<v-menu attach="#attachMenu" v-model="show" :right="true">
<v-list>
<v-list-item
v-for="(item, index) in items"
:key="index"
#click=""
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
show: false,
items: [
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me 2' },
],
}),
})
You almost got the right solution, the position with the right and left prop is inverted
So this is what you need to do:
<v-menu attach=".place" v-model="show" left>
I am using Vuetify 3 and I had the same problem. I solved it by using
<v-menu location="bottom end">
See :
https://next.vuetifyjs.com/en/components/menus/#location
https://next.vuetifyjs.com/en/components/overlays/#location-strategies
Related
I use standard v-list component of vuetifyjs. For create menu and show list of items.
But if I click second time the active element is hide. And I don't see active element of menu. It is bad for my menu. Link for example v-list
If use pug template below
v-list(dense)
v-list-item-group(color="success" v-model="selectedItem")
v-list-item(v-for="(gallery, key) in galleries" :key="key")
v-list-item-content
v-list-item-title(v-text="gallery")
I have solution. It needs watch variable and set force index by key of clicked item. If we have 'undefined' set force data method
Run examle solution Solution
Code below:
<template>
<div id="app">
<v-app id="inspire">
<v-card max-width="300" >
<v-list dense>
<v-list-item-group
v-model="selectedItem"
color="primary"
>
<v-list-item
v-for="(item, i) in items"
:key="i"
>
<v-list-item-content #click="setItem(i)">
<v-list-item-title v-text="item.text"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-app>
</div>
</template>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
index: 0,
selectedItem: 0,
items: [
{ text: 'Real-Time'},
{ text: 'Audience' },
{ text: 'Conversions'},
],
}),
watch: {
selectedItem() {
if (typeof this.selectedItem === 'undefined') {
setTimeout(() => {
this.selectedItem = this.index;
}, 500);
}
},
},
methods: {
setItem(index) {
this.index = index;
},
},
})
Another solution would be to disable the v-list-item that has the same index as the selectedItem
<template>
<div id="app">
<v-app id="inspire">
<v-card max-width="300" >
<v-list dense>
<v-list-item-group
v-model="selectedItem"
color="primary"
>
<v-list-item
v-for="(item, i) in items"
:key="i"
:disabled="i == selectedItem"
>
<v-list-item-content>
<v-list-item-title v-text="item.text"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-app>
</div>
</template>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selectedItem: 0,
items: [
{ text: 'Real-Time'},
{ text: 'Audience' },
{ text: 'Conversions'},
],
}),
})
I using a Vuetify.js for my project
when I used v-dialog component I got a problem that closing the v-dialog and open again.
here is my code.
<div #click="dialog=true">click here</div>
<v-dialog v-model="dialog">
<alert-popup />
</v-dialog>
data() {
return {
dialog : false
}
this is work when I open dialog first time but when I open again only can see the opacity black page
I don`t know which part is wrong. please reply this question. thanks
You didn't provide any code to reproduce the behavior. But I guess the you need provide <v-card> component inside the dialog component. If you don't provide that it just shows the opacity issue.Putting a v-card will eliminate the opacity issue.
<div id="app">
<v-app id="inspire">
<v-container>
<v-row justify="start">
<v-btn #click="openDialog">Open</v-btn>
<v-dialog v-model="dialog" max-width="300px">
<v-card>
<v-card-title>My Dialog</v-card-title>
<v-divider></v-divider>
<v-card-text>
This is text for dialog
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn x-small color="blue darken-1" text #click="dialog = false">Close</v-btn>
<v-btn x-small color="blue darken-1" text #click="dialog = false">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</v-container>
</v-app>
</div>
Here is the working code where there is a v-card component inside v-dialog.
I also see that you've used a custom component called alert-popup inside the v-dialog.If that is the case it needs to be refactored a bit to achieve what you're looking for.
Creating a separate component which you are already doing except <v-dialog>.
Emitting a close event from <alert-popup> component so that open/close and clicking open won't cause issue.
HTML:
<div id="app">
<v-app id="inspire">
<v-container>
<v-row justify="start">
<v-btn #click="openDialog">Open</v-btn>
<alert-popup :dialog="dialog" #close="closeMyDialog" />
</v-row>
</v-container>
</v-app>
</div>
Javascript:
let AlertPopup = Vue.component("AlertPopup", {
props: {
dialog: {
type: Boolean,
default: false
}
},
data: () => ({
open: false
}),
methods: {
close() {
this.open = false;
this.$emit("close");
}
},
watch: {
dialog(value) {
this.open = value;
}
},
created() {
this.open = this.dialog;
},
template: `
<v-dialog v-model="open" max-width="300px">
<v-card>
<v-card-title>My Dialog</v-card-title>
<v-divider></v-divider>
<v-card-text>
This is text for dialog
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn x-small color="blue darken-1" text #click="close">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
`
});
new Vue({
el: "#app",
vuetify: new Vuetify(),
components: {
AlertPopup
},
data: (vm) => ({
dialog: false
}),
computed: {},
methods: {
closeMyDialog() {
this.dialog = false;
},
openDialog() {
this.dialog = true;
}
}
});
If look at the AlertPopup component, it just take a prop named dialog and that we can pass from the main component, when button is clicked, it sets to true, which triggers the AlertPopup component.
NOTE:
If we don't emit the close event from the AlertPopup component, then the modal dialog will not open on the second time onwards. The reason for this behavior is the fact that, when the button is clicked from the parent component,which set the dialog to true and pass it to the AlertPopup and whatever things happens inside the AlertPopup remains inside that component and dialog property of parent component never changes.
Here is a working example of how the modal component are triggered from parent component. If we remove the close event, it will not open the modal second time.
Update: It turns out we are unnecessary complicating things, we don't
even need to emit an event, we can pass in the close function as a
prop and react to that, whenever the close button is clicked, it would
call the closeAlert().
Thanks to #Pratik149 for pointing out the bug, I have attached the click outside event handler.
Here is the completely re-factored code.
<div id="app">
<v-app id="inspire">
<v-container>
<v-row justify="start">
<v-btn #click="openDialog">Open</v-btn>
<alert-popup :dialog="dialog" #close="closeMyDialog" :close="closeMyDialog" />
</v-row>
</v-container>
</v-app>
</div>
And the component logic is updated as follow
let AlertPopup = Vue.component("AlertPopup", {
props: {
dialog: {
type: Boolean,
default: false
},
close: {
type: Function,
default: () => {}
}
},
data: () => ({
open: false
}),
methods: {
closeAlert() {
this.close();
}
},
template: `
<v-dialog v-model="dialog" max-width="300px" #click:outside="closeAlert">
<v-card>
<v-card-title>My Dialog</v-card-title>
<v-divider></v-divider>
<v-card-text>
This is text for dialog
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn x-small color="blue darken-1" text #click="closeAlert">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
`
});
new Vue({
el: "#app",
vuetify: new Vuetify(),
components: {
AlertPopup
},
data: (vm) => ({
dialog: false
}),
computed: {},
methods: {
closeMyDialog() {
debugger;
this.dialog = false;
},
openDialog() {
this.dialog = true;
}
}
});
Finally here is the updated codepen
I am trying to open a new page but it cannot. When I click on my home icon, it opens my home page and when click on dashboard it says
dashboard 'app' has been removed, use '' instead.
VToolbar>
at src/components/Navbar.vue
at src/App.vue
App.vue
template>
<nav>
<v-toolbar flat app>
<v-app-bar-nav-icon class="grey--text" #click="drawer =! drawer"></v-app-bar-nav-icon>
<v-toolbar-title class="text-uparcase grey--text">
<span class="font-weighted-light">Blue</span>
<span>Berry</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn flat color="grey">
<span>Home</span>
</v-btn>
</v-toolbar>
<v-navigation-drawer v-model="drawer" app class="indigo">
<v-list-item >
<v-list-item-content>
</v-list-item-content>
</v-list-item>
<v-divider></v-divider>
<v-list
dense
nav
>
<v-list-item
v-for="item in items"
:key="item.title"
router:to="item.route"
>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
</nav>
</template>
<script>
export default {
data() {
return {
drawer:false,
items: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard',route:'/' },
{ title: 'Photos', icon: 'mdi-image',route:'/photos'},
{ title: 'Home', icon: 'mdi-home' ,route:'/home'},
{ title: 'About', icon: 'mdi-help-box' ,route:'/about'}
],
right: null,
}
}
}
</script>
Try to nest every v-list items in a < router-link />
Like that
<router-link :to="{ name: item.title }">
<v-list-item>
...
</v-list-item>
</router-link>
Versions and Environment
Vuetify: 1.0.13
Vue: 2.5.13
Browsers: Chrome 67.0.3396.48
OS: Windows 10
Steps to reproduce the problem
Create a nav-drawer that has router-links in it.
In one of the links create a v-tab component that shows router-view for each tab.
Expected Behavior
In one of the links in the navigation-drawer, I have a link that connects to router view in one of it I have <v-tab> that connects to another router-view. Can you please help me?
Actual Behavior
The router-view for the tabs doesn't show up
Reproduction Link:
https://codepen.io/aabbrrm234/pen/deQjEe
<v-app id="inspire">
<v-navigation-drawer fixed :clipped="true" app v-model="drawer">
<v-list dense>
<template v-for="item in items">
<v-layout
row
v-if="item.heading"
align-center
:key="item.heading"
>
<v-flex xs6>
<v-subheader v-if="item.heading">
{{ item.heading }}
</v-subheader>
</v-flex>
<v-flex xs6 class="text-xs-center">
EDIT
</v-flex>
</v-layout>
<v-list-group
v-else-if="item.children"
v-model="item.model"
:key="item.text"
:prepend-icon="item.model ? item.icon : item['icon-alt']"
append-icon=""
>
<v-list-tile slot="activator">
<v-list-tile-content>
<v-list-tile-title>
{{ item.text }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile
v-for="(child, i) in item.children"
:key="i"
#click=""
>
<v-list-tile-action v-if="child.icon">
<v-icon>{{ child.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>
{{ child.text }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list-group>
<v-list-tile v-else router :to="item.link" :key="item.text">
<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>
{{ item.text }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-list>
</v-navigation-drawer>
<v-toolbar color="grey lighten-3" light app
:clipped-left="$vuetify.breakpoint.lgAndUp"
fixed
>
<v-toolbar-title style="width: 300px" class="ml-0 pl-3">
<v-toolbar-side-icon #click.stop="drawer = !drawer"></v-toolbar-side-icon>
<span class="hidden-xs-and-down">App</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-menu offset-y>
<v-icon slot="activator">more_vert</v-icon>
<v-list>
<v-list-tile #click="logout">
<v-list-tile-title>Logout</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-toolbar>
<v-content>
<!-- <v-container xs7 offset-xs2 offset-md2 offset-lg5> -->
<!-- <v-layout> -->
<!-- content goes here -->
<!-- <router-view class="mt-0.3"></router-view> -->
<!-- end content -->
<!-- </v-layout>
</v-container> -->
<!-- <v-container grid-list-xl text-xs-center> -->
<v-layout row wrap>
<v-flex xs12 sm12 md12>
<router-view></router-view>
</v-flex>
</v-layout>
<!-- </v-container> -->
</v-content>
<!-- <v-btn
fab
bottom
right
color="pink"
dark
fixed
#click.stop="dialog = !dialog"
>
<v-icon>add</v-icon>
</v-btn> -->
</v-app>
<script>
export default {
data: () => ({
// userId : authUser.id ,
dialog: false,
drawer: null,
items: [
{ icon: 'home', text: 'Home', link: '/' },
{ icon: 'motorcycle', text: 'Start Delivery', link: '/delivery' },
{ icon: 'people', text: 'Account Settings', link: '/account' },
{ icon: 'exit_to_app', text: 'Logout', link: 'logout' },
{ icon: 'map', text: 'Addresses', link: '/addresses' },
{ icon :'list', text : 'Parcels To Pack', link : '/to-pack'}
],
tabItems : [
{ text : 'Parcels to Pack', routeName : 'showParcelsToPack' },
{ text : 'Show Delivery Map', routeName : 'deliveryMap'},
],
// speed dial
direction: 'top',
fab: false,
fling: false,
hover: false,
tabs: null,
top: false,
right: true,
bottom: true,
left: false,
transition: 'slide-y-reverse-transition'
}),
props: {
source: String
},
methods: {
logout () {
window.location.href = '/auth/logout'
}
}
};
</script>
in one of the view :
<v-tabs icons-and-text centered dark color="red">
<v-tab to="/delivery">
Addresses
<v-icon>shopping_cart</v-icon>
</v-tab>
<v-tab :to="{ name : 'parcelsToPack', params : { id : 3 }}">
Parcels To Pack
<v-icon>list</v-icon>
</v-tab>
<v-tab :to="{ name : 'googleMap', params : { id : 3 } }">
Map<v-icon>map</v-icon>
</v-tab>
<v-tabs-slider color="white"></v-tabs-slider>
I added this another router-view to display the result of the tab.
<router-view name="main"></router-view>
</v-tabs>
To give you an overview something like this is all I needed :
video
Codepen
You need to use nested routes:
routes: [
{path: '/',name: 'Home', component: homePage,},
{path: '/user',name: 'User', component: userPage,
children: [
{path: '/profile',name: 'Profile', component: profile},
{path: '/activity',name: 'Activity', component: activity},
],
},
]
Components that you want to show in tabs should be included in your child routes.
Then place router-view inside v-tab-items (in case you use v-for, don't forget to use keys):
<v-tabs v-model="activeTab">
<v-tab to="profile">Profile</v-tab>
<v-tab to="activity">Activity</v-tab>
</v-tabs>
<v-tabs-items v-model="activeTab">
<v-tab-item id="profile">
<router-view></router-view>
</v-tab-item>
<v-tab-item id="activity">
<router-view></router-view>
</v-tab-item>
</v-tabs-items>
I have just started with Vue and found Vuetify ( and very impressed ) . I'm a bit of a newbie with node.js as well but some experience.
I am trying to find some examples on loading data from external API's into the vuetify datagrid - CRUD type stuff, reasonably large amounts of data paginated. The documentation in Vuetify is a little lacking in this regard. Should I be using Vuex?
If you want to call external API using REST, you'll need to use axios, which is a NPM package allowing you to make GET, POST and all that kind.
Let's use this online working API for our example. First, you need to get your data by calling this API. A good tutorial on Internet will show you more details, but let's use this code.
this.todos = axios.get('https://jsonplaceholder.typicode.com/todos/')
.then(response => { this.todos = response.data })
.catch(error => { console.log(error)});
Then you just have to use the datatable like in the documentation. Here is a CodePen to help you see, briefly, how I made the API call and then displayed it. It all comes from the official documentation, just modified to call a REST API. I'll put the code also here, in order to save it for future readers too.
<div id="app">
<v-app id="inspire">
<div>
<v-toolbar flat color="white">
<v-toolbar-title>Todos CRUD</v-toolbar-title>
<v-divider
class="mx-2"
inset
vertical
></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<v-btn slot="activator" color="primary" dark class="mb-2">New Item</v-btn>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container grid-list-md>
<v-layout wrap>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.userId" label="User ID"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.id" label="ID"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.title" label="Title"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-checkbox v-model="editedItem.completed" label="Completed?"></v-checkbox>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" flat #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
<v-data-table
:headers="headers"
:items="todos"
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td class="text-xs-right">{{ props.item.userId }}</td>
<td class="text-xs-right">{{ props.item.id }}</td>
<td class="text-xs-right">{{ props.item.title }}</td>
<td class="text-xs-right">{{ props.item.completed }}</td>
<td class="justify-center layout px-0">
<v-icon
small
class="mr-2"
#click="editItem(props.item)"
>
edit
</v-icon>
<v-icon
small
#click="deleteItem(props.item)"
>
delete
</v-icon>
</td>
</template>
<template slot="no-data">
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</div>
</v-app>
</div>
And then the associated JS.
new Vue({
el: '#app',
data: () => ({
dialog: false,
headers: [
{
text: 'User ID',
align: 'left',
sortable: false,
value: 'userId',
width: '10'
},
{ text: 'ID', value: 'id', width: '10' },
{ text: 'Title', value: 'title' },
{ text: 'Completed', value: 'completed' }
],
todos: [],
editedIndex: -1,
editedItem: {
userId: 0,
id: 0,
title: '',
completed: false
},
defaultItem: {
userId: 0,
id: 0,
title: '',
completed: false
}
}),
computed: {
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
}
},
watch: {
dialog (val) {
val || this.close()
}
},
created () {
this.initialize()
},
methods: {
initialize () {
this.todos = axios.get('https://jsonplaceholder.typicode.com/todos/')
.then(response => { this.todos = response.data })
.catch(error => { console.log(error)});
},
editItem (item) {
this.editedIndex = this.todos.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.todos.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.todos.splice(index, 1)
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () {
if (this.editedIndex > -1) {
Object.assign(this.todos[this.editedIndex], this.editedItem)
} else {
this.todos.push(this.editedItem)
}
this.close()
}
}
})