Related
In Vue2 this seems to be break:
<template>
<v-list v-model="itemSelected">
<v-list-item v-for="(item, i) in items" :key="i" #click="item.onClick"
>item.label</v-list-item>
</template>
<script>
export default {
name: 'myTestComponent'
data: () => ({
itemSelected,
items: [
{ label: 'Logout', onClick: this.onLogoutClick },
{ label: 'Settings', onClick: this.onSettingsClick },
{ label: 'Profile', onClick: this.onProfileClick },
]
}),
methods: {
onLogoutClick() {
console.log('LogoutClick')
},
onSettingsClick() {
console.log('SettingsClick')
},
onProfileClick() {
console.log('ProfileClick')
},
},
</script>
The error is that onLogoutClick is a null reference. This seems to indicate to me that the data property is created before the methods property.
Is there a way around this? Perhaps to pass the method names as a string and convert that reference to a method name at call time?
You can pass vue instance to data function, and call methods in onClick property :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: (vi) => ({
itemSelected: null,
items: [
{ label: 'Logout', onClick: vi.onLogoutClick },
{ label: 'Settings', onClick: vi.onSettingsClick },
{ label: 'Profile', onClick: vi.onProfileClick },
]
}),
methods: {
onLogoutClick() {
console.log('LogoutClick')
},
onSettingsClick() {
console.log('SettingsClick')
},
onProfileClick() {
console.log('ProfileClick')
},
},
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#6.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<v-list v-model="itemSelected">
<v-list-item v-for="(item, i) in items" :key="i" #click="item.onClick"
>{{item.label}}</v-list-item>
</v-list>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
I'm wondering if there is a way to hide a whole row/item in a vuetify data-table. I've read threads about hiding columns but not data-table items.
As per my understanding, You want to hide the specific rows from the <v-data-table>. If Yes, you can achieve that by using v-slot and manipulate the template.
Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
headers: [
{
text: 'Dessert',
value: 'name'
}
],
items: [
{
name: 'Frozen Yogurt',
show: true
},
{
name: 'Ice cream sandwich',
show: false
},
{
name: 'Eclair',
show: true
},
{
name: 'Cupcake',
show: false
}
]
}
},
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://unpkg.com/#mdi/font#6.x/css/materialdesignicons.min.css"/>
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="items"
class="elevation-1"
>
<template v-slot:item="props">
<template v-if="props.item.show">
<tr>{{ props.item.name }}</tr>
</template>
</template>
</v-data-table>
</v-app>
</div>
I think passing an empty array to your :items would fix your problem. or you can do this one :items="defaultVales || desserts".
In my opinion, the best way to handle this is to add a show property to your objects and use a computed property to hide and show items
Working example
Here it will hide column on click
new Vue({
el: '#app',
vuetify: new Vuetify(),
computed:{
showItems(){
return this.desserts.filter(x => x.show)
}
},
data() {
return {
headers: [
{ text: 'Name', value: 'name' },
{ text: 'Score', value: 'score' },
],
desserts: [{
name: "Frozen",
score: 66,
show: true,
},
{
name: "Tom",
score: 100,
show: true,
},
{
name: "Eclair",
score: 100,
show: true,
},
{
name: "Frozen",
score: 89,
show: true,
},
]
}
},
methods: {
hideColumn(col){
col.show = false
}
}
})
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/vuetify#2.3.4/dist/vuetify.min.css'>
<script src='https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js'></script>
<script src='https://cdn.jsdelivr.net/npm/vuetify#2.3.4/dist/vuetify.min.js'></script>
<div id="app">
<v-data-table
:items="showItems"
:headers="headers"
#click:row="hideColumn"
>
</v-data-table>
</div>
I have a v-treeview where a node contains ~2000 children. I need to apply a filter. Opening the node currently takes ~3 seconds. Closing the node takes ~15 seconds. This is unacceptable.
Without the filter applied, opening and closing it nearly instantaneous. Closing the node does not appear to call the filter function again.
The problem is trivially reproducible using the Searching a Directory example as the base.
I have a CodePen here https://codepen.io/james-hudson3010/pen/JjXYgZj
<div id="app">
<v-app id="inspire">
<v-card
class="mx-auto"
max-width="500"
>
<v-sheet class="pa-4 primary lighten-2">
<v-text-field
v-model="search"
label="Search Company Directory"
dark
flat
solo-inverted
hide-details
clearable
clear-icon="mdi-close-circle-outline"
></v-text-field>
<v-checkbox
v-model="caseSensitive"
dark
hide-details
label="Case sensitive search"
></v-checkbox>
</v-sheet>
<v-card-text>
<v-treeview
:items="items"
:search="search"
:filter="filter"
:open.sync="open"
>
</v-treeview>
</v-card-text>
</v-card>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{
id: 1,
name: 'Vuetify Human Resources',
children: [
{
name: 'Core team',
children: [
{ id: 2, name: 'John-0' },
{ id: 3, name: 'John-1' },
{ id: 4, name: 'John-2' },
{ id: 5, name: 'John-3' },
{ id: 6, name: 'John-4' },
{ id: 7, name: 'John-5' },
//
// Some items removed due to length
//
{ id: 2000, name: 'John-1998' },
{ id: 2001, name: 'John-1999' },
],
}
],
},
],
open: [1, 2],
search: null,
caseSensitive: false,
}),
computed: {
filter () {
return this.caseSensitive
? (item, search, textKey) => item[textKey].indexOf(search) > -1
: undefined
},
},
})
This bug has been fixed with Vuetify 2.3.8.
https://github.com/vuetifyjs/vuetify/releases/tag/v2.3.8
So i am making a small list in which i have a list of car companies and an option for users to select their favorite one. I have an array of objects which returns the name and a certain property named starred. I have a method setStarred which sets the starred of selected to true. But what i am trying to achieve is to have the option to only select one at a time, so if i select BMW, the starred for other two should be toggled to false. Right now i can toggle all of them at the same time.
Please check this working codepen.
Check out the working example below:-
new Vue({
el: '#app',
data() {
return {
cars: [{
name: 'Toyota',
starred: false
},
{
name: 'BMW',
starred: false
},
{
name: 'Ford',
starred: false
}
]
}
},
methods: {
setStarred(item) {
item.starred = !item.starred
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<link rel="stylesheet" href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'>
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center column>
<v-flex xs6 v-for="(car,index) in cars" :key="index">
<h2>{{car.name}}
<v-icon :color="car.starred ? 'primary': '' " #click="setStarred(car)">star_border
</v-icon>
</h2>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
You have to loop throw other array items and set their starred property to false. Or, you can store a data variable to indicate the index of the object which is currently stared.
new Vue({
el: '#app',
data() {
return {
cars: [{
name: 'Toyota',
starred: false
},
{
name: 'BMW',
starred: false
},
{
name: 'Ford',
starred: false
}
],
lastStarredIndex: null
}
},
methods: {
setStarred(index) {
if (this.lastStarredIndex === index) {
// toggle same item
this.cars[index].starred = !this.cars[index].starred;
if (this.cars[index].starred) this.lastStarredIndex = index;
else this.lastStarredIndex = null;
} else {
// disable last stared item
if (this.lastStarredIndex !== null) this.cars[this.lastStarredIndex].starred = false;
// set the new one
this.cars[index].starred = true;
this.lastStarredIndex = index;
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<link rel="stylesheet" href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'>
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center column>
<v-flex xs6 v-for="(car,index) in cars" :key="index">
<h2>{{car.name}}
<v-icon :color="car.starred ? 'primary': '' " #click="setStarred(index)">star_border
</v-icon>
</h2>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
new Vue({
el: '#app',
data() {
return {
cars: [{
name: 'Toyota',
starred: false
},
{
name: 'BMW',
starred: false
},
{
name: 'Ford',
starred: false
}
]
}
},
methods: {
setStarred(item) {
// Set all to false
_.each(this.cars, function(value, key) {
value.starred = false;
});
item.starred = true
}
}
})
Solution
new Vue({
el: "#app",
data() {
return {
cars: [
{
name: "Toyota",
starred: false,
},
{
name: "BMW",
starred: false,
},
{
name: "Ford",
starred: false,
},
],
};
},
methods: {
setStarred(item) {
this.cars.forEach((car) => (car.starred = car.name === item.name));
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<link rel="stylesheet" href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'>
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center column>
<v-flex xs6 v-for="(car,index) in cars" :key="index">
<h2>{{car.name}}
<v-icon :color="car.starred ? 'primary': '' " #click="setStarred(car)">star_border
</v-icon>
</h2>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
<v-icon :color="car.starred ? 'primary': '' " #click="setStarred(index)">star_border
</v-icon>
methods: {
setStarred(index) {
this.cars.map(i => {
i.starred = false
})
this.cars[index].starred = true
}
}
I have been looking for a way to drag and drop rows on a Bootstrap Vue table.
I was able to find a working version here: Codepen
I have tried to implement this code to my own table:
Template:
<b-table v-sortable="sortableOptions" #click="(row) => $toast.open(`Clicked ${row.item.name}`)" :per-page="perPage" :current-page="currentPage" striped hover :items="blis" :fields="fields" :filter="filter" :sort-by.sync="sortBy" :sort-desc.sync="sortDesc" :sort-direction="sortDirection" #filtered="onFiltered">
<template slot="move" slot-scope="row">
<i class="fa fa-arrows-alt"></i>
</template>
<template slot="actions" slot-scope="row">
<b-btn :href="'/bli/'+row.item.id" variant="light" size="sm" #click.stop="details(cell.item,cell.index,$event.target)"><i class="fa fa-pencil"></i></b-btn>
<b-btn variant="light" size="sm" #click.stop="details(cell.item,cell.index,$event.target)"><i class="fa fa-trash"></i></b-btn>
</template>
<template slot="priority" slot-scope="row">
<input v-model="row.item.priority" #keyup.enter="row.item.focussed = false; updatePriority(row.item), $emit('update')" #blur="row.item.focussed = false" #focus="row.item.focussed = true" class="form-control" type="number" name="priority" >
</template>
</b-table>
Script:
import Buefy from 'buefy';
Vue.use(Buefy);
const createSortable = (el, options, vnode) => {
return Sortable.create(el, {
...options
});
};
const sortable = {
name: 'sortable',
bind(el, binding, vnode) {
const table = el.querySelector('table');
table._sortable = createSortable(table.querySelector('tbody'), binding.value, vnode);
}
};
export default {
name: 'ExampleComponent',
directives: { sortable },
data() {
let self = this;
return {
blis: [],
currentPage: 1,
perPage: 10,
pageOptions: [ 5, 10, 15 ],
totalRows: 0,
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
sortableOptions: {
chosenClass: 'is-selected'
},
filter: null,
modalInfo: { title: 'Title', content: 'priority' },
fields: [
{
key: 'move',
sortable: true
},
///...rest of the fields
]
}
};
Now I have been getting this error: Error in directive sortable bind hook: "TypeError: Cannot read property 'querySelector' of null"
Why is it not able to find the <tbody> ?
Edit: https://jsfiddle.net/d7jqtkon/
In line const table = el.querySelector('table'); you are trying to get the table element. The var el is the table element. That is why it return null when you use querySelector
after assigning the correct table variable the error disappears
const table = el;
table._sortable = createSortable(table.querySelector("tbody"), binding.value, vnode);
Link to working fiddle
new Vue({
el: "#app",
directives: {
sortable: {
bind(el, binding, vnode) {
let self =el
Sortable.create(el.querySelector('tbody'),{
...binding.value,
vnode:vnode,
onEnd: (e) => {
let ids = el.querySelectorAll("span[id^=paper_]")
let order = []
for (let i = 0; i < ids.length; i++) {
let item = JSON.parse(ids[i].getAttribute('values'))
//extract items checkbox onChange v-model
let itemInThisData = vnode.context.items.filter(i => i.id==item.id)
order.push({
id:item.id,
paper: item.paper,
domain:item.domain,
platform: item.platform,
country:item.country,
sort_priority: item.sort_priority,
selectpaper:itemInThisData[0].selectpaper
})
}
binding.value = []
vnode.context.items = []
binding.value = order
vnode.context.items = order
console.table(vnode.context.items)
},
});
},
}
},
mounted() {
this.totalRows = this.items?this.items.length: 0
},
methods:{
onFiltered(filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
},
log(){
console.table(this.items)
console.log(this)
},
},
data(){
return {
rankOption:'default',
totalRows: 1,
currentPage: 1,
filter: null,
filterOn:[],
sortBy:'paper',
sortDesc: false,
sortableOptions: {
chosenClass: 'is-selected'
},
perPage: this.results_per_page==='Todo' ? this.items.length : this.results_per_page?this.results_per_page:50,
pageOptions: [10, 50, 100, 500,'Todo'],
sortDirection: 'asc',
fields : [
{ key: 'paper', label: 'Soporte', sortable: true},
{ key: 'domain', label: 'Dominio', sortable: true},
{ key: 'platform', label: 'Medio', sortable: true},
{ key: 'country', label: 'País', sortable: true},
{ key: 'sort_priority', label: 'Rank', sortable: true},
{ key: 'selectpaper', label: 'Selección', sortable: true},
],
items : [
{
id:12,
paper: 'Expansion',
domain:'expansion.com',
platform: 'p',
country:'España',
sort_priority: '',
selectpaper:false
},
{
id:13,
paper: 'El economista',
domain:'eleconomista.es',
platform: 'p',
country:'España',
sort_priority: '',
selectpaper:false
},
{
id:14,
paper: 'El país',
domain:'elpais.es',
platform: 'p',
country:'España',
sort_priority: '',
selectpaper:false
}
]
}
}
})
<div id="app">
<template id="">
<b-table
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
v-sortable="items"
show-empty
small
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:filterIncludedFields="filterOn"
:sort-direction="sortDirection"
#filtered="onFiltered"
>
<template v-slot:cell(selectpaper)="row">
<span :id="'paper_'+row.item.id" :values="JSON.stringify(row.item)"></span>
<b-form-group>
<input type="checkbox" #change="log" v-model="row.item.selectpaper" />
</b-form-group>
</template>
<template v-slot:cell(sort_priority)="row" v-if="rankOption==='foreach-row'">
<b-form-group>
<b-form-input type="number" #change="log"
size="sm" placeholder="Rank" v-model="row.item.sort_priority">
</b-form-input>
</b-form-group>
</template>
</b-table>
</template>
</div>
<script src="//unpkg.com/vue#latest/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs#latest/Sortable.min.js"></script>