I have a table where I have a number of items shown all grouped by a string property.
By default these groups are all expanded.
https://vuetifyjs.com/en/components/data-tables/#grouped-rows
Is there anyway to collapse all the groups or expand them at once ?
Ie have a collapse all button above the table. I have search but can't find a solution.
Thanks
The latest Vuetify does pass the isOpen and toggle values in the group.header slot. You could customize this slot to track $refs for each group that can then be bound to a toggle all (or expand/collapse all) function....
<template v-slot:group.header="{ group, headers, toggle, isOpen }">
<td :colspan="headers.length">
<v-btn #click="toggle" small icon :ref="group">
<v-icon v-if="isOpen">mdi-chevron-up</v-icon>
<v-icon v-else>mdi-chevron-down</v-icon>
</v-btn>
{{ group }}
</td>
</template>
methods: {
toggleAll () {
Object.keys(this.$refs).forEach(k => {
this.$refs[k].$el.click()
})
}
}
Demo: https://codeply.com/p/ys4Df2OLiE
Codeply-er answer may work but I didn't want to track $refs for each group. In the end I add this hack
private expandAll() {
const self = this;
for (const name of Object.keys(self.$refs.expandableTable.openCache)) {
self.$refs.expandableTable.openCache[name] = true;
}
}
private collapseAll() {
const self = this;
for (const name of Object.keys(self.$refs.expandableTable.openCache)) {
self.$refs.expandableTable.openCache[name] = false;
}
}
This probably relies on an internal method openCache so not ideal.
Related
I have a couple of dialogs which are created with v-for (exchangeTypeAbbreviation and exchangeType come from there). When I click on the activator button, the dialog opens and the value in the object I use for storing the dialogs' state is updated to "true".
But when I click the cancel or save button, the dialog won't close, although the object's value is updated to "false".
<v-list-item>
<v-dialog
max-width="400"
v-model="dialogs[exchangeTypeAbbreviation]"
>
<template v-slot:activator="{ on }">
<v-list-item v-on="on">
<v-icon class="pr-4">
mdi-plus
</v-icon>
Add Product Flow
</v-list-item>
</template>
<v-card>
<v-card-title>Add Product Flow</v-card-title>
<v-card-subtitle
v-text="exchangeType"
></v-card-subtitle>
<v-card-actions>
<v-btn
#click="
dialogs[exchangeTypeAbbreviation] = false;
createUnitProcessExchange(
exchangeTypeAbbreviation
);
"
>Save</v-btn
>
<v-btn
#click="dialogs[exchangeTypeAbbreviation] = false"
>Cancel</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
</v-list-item>
<script>
export default {
name: 'Activities',
data: () => ({
dialogs: {},
exchangeTypes: {},
unitProcessExchangesOptions: null,
}
}),
mounted() {
Promise.all([
this.loadUnitProcessExchangeOptions()
])
},
methods: {
async loadUnitProcessExchangeOptions() {
return this.$api
.options('/unitprocessexchanges/', {
headers: {
Authorization: 'Token ' + localStorage.getItem('token')
}
})
.then(response => {
this.unitProcessExchangesOptions = response.data.actions.POST
for (const exchangeType of this.unitProcessExchangesOptions
.exchange_type.choices) {
this.exchangeTypes[exchangeType.value] = exchangeType.display_name
this.dialogs[exchangeType.value] = false
}
})
},
async createUnitProcessExchange(exchangeTypeAbbreviation) {
this.newUnitProcessExchange.activity = this.activities[
this.selectedActivity
].url
this.newUnitProcessExchange.exchange_type = exchangeTypeAbbreviation
this.dialogs[exchangeTypeAbbreviation] = false
// eslint-disable-next-line no-debugger
debugger
}
}
}
</script>
I was able to figure out why it doesn't work. Due to limitations in JavaScript, Vue.js has some difficulties to observe changes in Objects and Arrays. This is documented here.
In my case, I added nested variables inside my "dialogs" variable by assigning them directly, e.g. like this
this.dialogs[index] = false
However, this creates a sub-element which can't be tracked by Vue.js. To make sure that changes on this element can be tracked, it either has to be pre-defined from the beginning or needs to be set by using the Vue.$set command. Always using the following command, solved the issue for me:
this.$set(dialogs, index, false)
I think the first problem is you are trying to change the object with an array notation i.e array[0] but it should be a dot notation with object property, in your case it would be dialogs.exchangeTypeAbbreviation = false.
With that one more problem would be that property doesn't exist so in
data: () => ({
dialogs: {exchangeTypeAbbreviation:Boolean},
exchangeTypes: {},
unitProcessExchangesOptions: null,
}
}),
with this now you can set the value of exchangeTypeAbbreviation.
I have a list of items in a v-for loop. I have a function on #click that will delete the item but I want to give it a class when I click it to change the background color for a short time so the user knows they clicked that item and it's deleting. In my deleteItem function, I set deletingItem to true but that obviously creates an issue because it will apply that class to all the divs in the in v-for. What is the best way to solve it so that it only gets applied to the div I click on?
<div :class="{'box': true, 'deleting-item': deletingItem}" v-for="(item,index) in items">
<div #click="deleteItem(item,index)>Delete</div>
</div>
You need to save the clicked item in a data property
<template>
<div>
<div v-for="(item, index) in items">
<button #click="deleteItem(index)" :class="{'delete-in-progress-class': index === indexOfDeletedItem}> {{item}} </button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: ['a', 'b', 'c']
indexOfDeletedItem: null
}
},
methods: {
deleteItem(index) {
this.indexOfDeletedItem = index;
setTimeout(() => { this.items.splice(index, 1); this.indexOfDeletedItem = null }, 1000); //delete item after 1s
}
}
}
</script>
<style>
.delete-in-progress-class {
background: red;
}
</style>
Obviously the solution above is naive. It'll probably go crazy if the user wants to delete many items in a short time, since the indices of an array shift around when you delete something.
But hopefully it'll give you an idea of how to conditionally apply styles in a list.
Look at this : https://codepen.io/positivethinking639/pen/mddejJN
My script of vue like this :
data: () => ({
modalTest: false,
dateTest: null,
time: null,
allowedTimes: ['8:30 am','9:00am','10:30am','1:30pm','3:30 pm']
}),
methods: {
saveData() {
this.$refs.dialogTest.save(this.dateTest)
},
allowedDates: val => parseInt(val.split('-')[2], 10) % 2 === 0,
setTime(time) {
this.time = time
}
I want before call datepicker, I call ajax first
How can I do it?
#Max proposal is not fully answers the question.
Let's add new data property which will trigger the show of calendar component:
isAjaxCompl: false,
Move the button out of template to directly change dialog v-model:
<v-btn color="success" #click="openDialog()">call date</v-btn>
Make the function which will be fired on dialog open:
openDialog() {
this.modalTest = true;
axios.get('https://reqres.in/api/users?delay=1').then((response) => {
this.isAjaxCompl = true;
})
},
Finally, add v-if which will show calendar component only when axios get the response:
<v-date-picker v-if="isAjaxCompl" v-model="dateTest" scrollable :allowed-dates="allowedDates">
Link to the corresponding CodePen:
https://codepen.io/RobbyFront/pen/RwwWewM
You need to move the dialog button outside and add a #click method for showing the dialog
Your Code
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">call date</v-btn>
</template>
New Code
Html
<v-btn color="success" dark #click="showDate">call date</v-btn>
Code
showDate(){
console.log("Ajax calling");
this.modalTest = true;
}
Here the pen
I am populating a dropdown from a computed method that returns an array of store objects. Then in the vue-select, I am passing this in as options and having the option.address show in the dropdown. That is working as expected but when clicking a dropdown option, the box doesn't show the value -- it just remains blank.
computed: {
storeLocationsArray: function() {
let arr = [];
this.storeLocations.forEach((location,index) => {
arr.push({id: index, address: location.address})
})
return arr;
}
}
<v-select
v-model="selectedPickupLocation"
:options="storeLocationsArray"
>
<template class="single-option" slot="option" slot-scope="option">
{{option.address}}
</template>
</v-select>
You can use label to display address instead of slot
<v-select
v-model="selectedPickupLocation"
:options="storeLocationsArray"
label="address"
>
</v-select>
I would like to know if there is a way in which I can dynamically apply classes targeting a specific cell in a Buefy table. As an example, the following is the code I am working on:
Template:
<b-table :data="status.peerStatus">
<template slot-scope="props">
<b-table-column :class="classObject" v-for="(column, index) in columns" :key="index"
:label="column.label" :visible="column.visible" :width="200">
{{ props.row[column.field] }}
</b-table-column>
</template>
</b-table>
Script:
computed: {
classObject() {
return {
"has-background-info": true
};
}
Right now, the whole row is getting highlighted in blue color due the has-background-info being set to true.
What I would like to do, however, is to target only a particular cell and apply class conditionally by passing the value of the cell like so.
Right now, what I am trying is passing the value of the cell to classObject like this
<b-table-column :class="classObject(props.row[column.field])" v-for="(column, index) in columns" :key="index"
And trying to set the class accordingly
computed: {
classObject(cellValue) {
return {
"has-background-info": cellValue === "YES" ? true : false;
};
}
However, the above doe not work. Is there any other way of doing this?
You should put it in method instead of computed
methods: {
classObject(cellValue) {
return {
"has-background-info": cellValue === "YES" ? true : false;
};
}
}