I'm currently implementing a data table using Vuetify, but I have stumbled into a problem while trying to react to a click on the "select-all" button in a data table. Right now, when the select-all button is clicked, the currently visible rows are selected (which is exactly what I want). However, I would like to be notified of the user clicking on this select-all button.
My plan is to provide the user with a "select everything" button, once the user clicks this "select-all" checkbox, but I can't seem to find a way (without having to reside to hacks) to get notified of a click on the "select-all" button.
There is a toggle-select-all method.
<div id="app">
<v-app id="inspire">
<v-data-table
:value="selected"
#toggle-select-all="selectAll"
:items-per-page="itemsPerPage"
:headers="headers"
:items="desserts"
item-key="name"
show-select
class="elevation-1"
>
</v-data-table>
<v-dialog>
<v-card>
</v-card>
</v-dialog>
</v-app>
</div>
Javascript is below:
new Vue({
el: '#app',
vuetify: new Vuetify(),
methods: {
selectAll(event) {
if (event.status) {
alert('selected all')
} else {
alert('deselected all')
}
}
},
data () {
return {
selected: [],
itemsPerPage: 10,
headers: ['headers', 'data'],
desserts: ['your', 'data']
}
}
})
You can do this by using :value and #input on the Vuetify table rather than v-model. Check if the selected array is equal to items per page when the user selects something.
<div id="app">
<v-app id="inspire">
<v-data-table
:value="selected"
#input="enterSelect($event)"
:items-per-page="itemsPerPage"
:headers="headers"
:items="desserts"
item-key="name"
show-select
class="elevation-1"
>
</v-data-table>
<v-dialog>
<v-card>
</v-card>
</v-dialog>
</v-app>
</div>
Javascript is below:
new Vue({
el: '#app',
vuetify: new Vuetify(),
methods: {
enterSelect(values) {
if (values.length == this.itemsPerPage) {
alert('selected all')
}
}
},
data () {
return {
selected: [],
itemsPerPage: 10,
headers: ['headers', 'data'],
desserts: ['your', 'data']
}
}
})
Working example here: https://codepen.io/CodingDeer/pen/QWLyaog?editors=1010
Related
There is scenario where there is right drawer in which grade dropdown with list grade1, grade2, grade3 etc is been displayed and on all pages I need v-alert notification strip to display total count of student. So, for example from right drawer if I select grade3 then on v-alert strip total student of grade 3 will be displayed. If I change the dropdown value in right drawer to grade1 then total number of student for grade 1 will be reflected on v-alert on all pages.
NOTE - v-alert strip should be displayed on all pages just like navbar.
How can I achieve this?
Should I use stores for this?
I am not sure about the component structure you have but I am demoing based on the assumption that you have both drawer and v-alert in a master page (parent page) and other routes are loading inside it. If my understanding is correct. You can give a try to this :
Vue.component('child', {
data() {
return {
items: [
{ title: 'Grade 1', icon: 'mdi-view-dashboard', count: 5 },
{ title: 'Grade 2', icon: 'mdi-view-dashboard', count: 10 },
{ title: 'Grade 3', icon: 'mdi-view-dashboard', count: 15 },
],
}
},
template: `<v-navigation-drawer permanent>
<v-divider></v-divider>
<v-list dense nav>
<v-list-item v-for="item in items" :key="item.title" link #click="getStudentCount(item.title)">
<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>`,
methods: {
getStudentCount(title) {
this.$emit('student-count', this.items.find(obj => obj.title === title).count)
}
}
});
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
right: null,
studentCount: 0
}
},
methods: {
getStudentCount(e) {
this.studentCount = e
}
}
});
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.13/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.13/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
<div id="app">
<v-app id="inspire">
<v-card height="400" width="256" class="mx-auto">
<v-alert border="left" color="indigo" dark>
Count : {{ studentCount }}
</v-alert>
<child #student-count="getStudentCount"></child>
</v-card>
</v-app>
</div>
I have an array cars which returns me the names and each of them has a property starred which i want to toggle true and false back and forth. However i want to set starred to true for only one of them at a time. So i created a method setStarred and inside the method, i am using a map to set others to false. However i am able to set the starred to true however i am not able to set it back to false.
Please check this Codepen
This is working example
new Vue({
el: "#app",
data() {
return {
cars: [{
name: "Toyota",
starred: false
},
{
name: "BMW",
starred: false
},
{
name: "Ford",
starred: false
}
]
};
},
methods: {
setStarred(index) {
this.cars.map((i) => {
i.starred = false;
});
this.cars[index].starred = !this.cars[index].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" />
<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>
Essentially i am trying to set the selected back to false. Any help will be appreciated. Thank you
Try this:
this.cars[index].starred = !this.cars[index].starred;
this.cars.map((i) => {
if(i.name!=this.cars[index].name)
i.starred = false;
});
I prefer saving the 'starred' state of the target first, then toggle it later.
If so, you don't need to put one if statement in the for-loop. Though in this case, it doesn't improve the performance a lot, but I believe avoid too many if from for-loop is one good habit.
Below is the example:
new Vue({
el: "#app",
data() {
return {
cars: [{
name: "Toyota",
starred: false
},
{
name: "BMW",
starred: false
},
{
name: "Ford",
starred: false
}
]
};
},
methods: {
setStarred(index) {
let cur = this.cars[index].starred
this.cars.forEach((i) => {
i.starred = false;
});
this.cars[index].starred = !cur;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.18/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" />
<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>
I have a text field in which you can input the field and on Click it adds a value to an array which renders the chips. Now each chips has a value property and a toggle property. By Default every new chip has toggle set to false. Now i can add as well as delete chips. The chips are deleted by using the deleteChips method. What i am trying to do is when there is only one chip left, the chip's toggle value should change dynamically to false or true depending on whatever it was at the time. So basically toggle = !toggle.
I have tried using the array.length but wasn't able to get it working.
Here is a sample pen
Here is the sample code:-
new Vue({
el: "#app",
data() {
return {
inputValue: "",
inputArray: [],
selectedChip: ""
};
},
methods: {
createChips() {
this.inputArray.unshift({
value: this.inputValue,
toggle: false
});
this.inputValue = "";
},
deleteChips(chip) {
let index = this.inputArray.filter((el) => el.chip === chip);
this.inputArray.splice(index, 1);
},
chipSelection(item) {
this.selectedChip = item;
},
toggleChip(item) {
item.toggle = true;
}
}
});
<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" />
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center>
<v-flex xs6>
<v-text-field v-model="inputValue"></v-text-field>
<v-btn #click="createChips">Click Me</v-btn>
<div v-if="inputArray.length > 0">
<div v-for="chip in inputArray">
<v-chip :key="chip.value" close #click="chipSelection(chip.value)" #input=deleteChips(chip)>
<v-avatar>
<v-icon #click="toggleChip(chip.value)">
account_circle
</v-icon>
</v-avatar>
<span v-if="selectedChip === chip.value">{{chip.value}}</span>
</v-chip>
</div>
</div>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
tbh, I can't really follow your intent, but based on the comments, perhaps you want to watch the inputArray and execute code based on changes to its length.
watch: {
inputArray(newValue, oldValue) {
if ((newValue.length !== oldValue.length) && (newValue.length === 1)) {
this.inputArray[0].toggle = !this.inputArray[0].toggle;
}
}
}
I want to use Vuetify's v-list-item-group component for my Vue app. This list represents nodes that are related to a graph. I can select none, some or all of them and delete the selected ones.
For a better user experience I want to provide a "select all / deselect all" checkbox at the top next to the header. If only some nodes are selected, the checkbox should render the indeterminate state.
Currently this is the code I'm using
<div id="app">
<v-app id="inspire">
<v-list>
<v-list-item>
<v-list-item-action>
<v-checkbox :indeterminate="someNodesSelected" :input-value="allNodesSelected" #click="toggleCompleteSelection" />
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="graphWithNodes.name"></v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn icon :disabled="noNodesSelected" #click="deleteNodes">
<v-icon color="error">mdi-delete</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
<v-list-item-group v-model="selectedNodeIds" multiple>
<v-list-item v-for="node in graphWithNodes.nodes" :key="node.id" :value="node.id">
<template v-slot:default="{ active, toggle }">
<v-list-item-action>
<v-checkbox :input-value="active" :true-value="node.id" #click="toggle" />
</v-list-item-action>
<v-list-item-content>
<v-list-item-subtitle v-text="node.id"></v-list-item-subtitle>
</v-list-item-content>
</template>
</v-list-item>
</v-list-item-group>
</v-list>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
selectedNodeIds: [],
graphWithNodes: {
id: 1,
name: "The graph",
nodes: [{
id: 1,
graphId: 1
}, {
id: 2,
graphId: 1
}]
},
}
},
computed: {
noNodesSelected() {
return this.selectedNodeIds.length === 0;
},
someNodesSelected() {
return this.selectedNodeIds.length > 0 && !this.allNodesSelected;
},
allNodesSelected() {
return (
this.selectedNodeIds.length === this.graphWithNodes.nodes.length
);
}
},
methods: {
deleteNodes(nodeIds) {
for (const nodeId of this.selectedNodeIds) {
this.deleteNode(nodeId);
}
this.selectedQueueIds = [];
},
deleteNode(id) {
this.graphWithNodes.nodes = this.graphWithNodes.nodes.filter(node => node.id !== id);
},
toggleCompleteSelection() {
if(this.noNodesSelected || this.someNodesSelected) {
this.selectedNodeIds = this.graphWithNodes.nodes.map(node => node.id);
} else {
this.selectedNodeIds = [];
}
}
}
})
If you want to play around I created a codepen for this
https://codepen.io/magicfoobar/pen/RwPBNmV?editors=1010
So the problem I have is that when I click on the header checkbox the function toggleCompleteSelection gets executed twice and I can't figure out why.
Does someone know why the header checkbox is broken and how to fix it?
Thanks in advance
It works if you change the checkbox trigger from #click to #change
<v-checkbox
:indeterminate="someNodesSelected"
:input-value="allNodesSelected"
#change="toggleCompleteSelection" />
Just add .stop after the click and it works.
<v-checkbox :indeterminate="someNodesSelected" :input-value="allNodesSelected" #click.stop="toggleCompleteSelection" />
codepen - https://codepen.io/Pratik__007/pen/MWwBKRL?editors=1010
I'm not sure that you need to run toggleCompleteSelection directly from the checkbox.
I would achieve the "select all" functionality with a Watcher, see https://codepen.io/joffff/pen/06cd75ea651660d13d4ddc288b8448d7?editors=1010
I am using vuetify and trying to create a method to add chips from the dropdown. Now i got most of the logic down but as you can see, the method multipleSelection does not fire off on the enter key but works fine onClick.
Here is a demo for the codepen.
So the method multipleSelection should fire off on the enter key.
new Vue({
el: '#app',
data() {
return {
arr: [],
items: [{
text: 'Foo',
value: 'foo'
},
{
text: 'Bar',
value: 'bar'
},
{
text: 'biz',
value: 'buzz'
},
{
text: 'buzz',
value: 'buzz'
}
],
}
},
methods: {
multipleSelection(item) {
this.arr.push(item)
console.log(this.arr)
},
deleteChip(item) {
console.log('delete')
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr)
}
},
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<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>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox :items="items" label="Add Multiple Chips" multiple small-chips solo deletable-chips :value="arr">
<template v-slot:item="{ index, item }">
<v-list-tile-content #click.stop.prevent="multipleSelection(item)">
{{item.text}}
</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info" #click:close="deleteChip(item)">
{{ item.text }}
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
Since multipleSelection() is not being called from #keypress on v-slot:item, likely it's not where the event is being captured.
Taking a look at events on Vue Dev Tools, can see input $emit by <VCombobox> is the first one after pressing Enter.
So the following will get it, but this seems to mess with the position in the list for some reason I don't understand.
<v-combobox
...
#input.capture="(item) => multipleSelection(item)"
>
Better to add a listener,
mounted() {
this.$refs.combobox.$on('input', (items) => {
const item = items[items.length -1]; // getting all selected, so take the last
this.multipleSelection(item)
})
},
Note, I tested this on a local project with Vuetify v1.5.14.
Looks like you need #keyup
<v-list-tile-content
#keyup.enter.prevent="multipleSelection(item)" #click.stop.prevent="multipleSelection(item)">{{item.text}}
</v-list-tile-content>
Not sure about keypress....Vue docs show #keyup
https://v2.vuejs.org/v2/guide/events.html#Key-Modifiers