Our tables pagination and sorting are all done on the server, but I still want to allow the user to click column headers to sort. I need to access the tables sort options when the sorting changes. It seems the events are split
update:sort-by
update:sort-desc
I could create two methods, have the first event set the column (sort-by) value, and then the second method actually trigger the sort. But that sounds horrible and prone to race conditions or future bugs.
It would be better if the sort-by included the desc/asc data as well. I tried creating a ref to the table, but for some reason the properties containing the sort information are empty.
The event update:options contains all the necessary information, but that could fire for reasons other than sorting which isn't ideal either.
So I'm not sure if I'm missing something here. Is there a better way to accomplish this?
<v-data-table
ref="contractItemTable"
:headers="headers"
:items="contracts"
:disable-sort="isLoadingPage"
:server-items-length="tableTotal"
disable-pagination
hide-default-footer
@click:row="navigateToContract"
single-select
@update:sort-by="sortTable"
@update:sort-desc="sortTable"
item-key="id.id">
And the JS
public sortTable(event) {
console.debug(this.$refs.contractItemTable.sortBy);
console.debug(this.$refs.contractItemTable.sortDesc);
}
No matter what I click, the sortBy and sortDesc properties are empty. If I use the options event, the event contains the correct sortBy and sortDesc. But as stated above, not exactly the event I want to use. I have it working, but I have to "ignore" the initial load options event since it's not a valid time for this method to fire.
You're mostly there (and found your question while trying to sort out the best way to apply sorting to derived/computed column-items myself) ... just need to bind the v-data-table sort-by and sort-desc attributes to data elements so that you can refer to them in your sortTable function:
<v-data-table
...
:items="contracts"
:headers="headers"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
@update:sort-by="sortTable"
@update:sort-desc="sortTable"
...
>
export default {
data() {
contracts: [],
sortBy: 'contractDate', // make sure this matches headers[].value
sortDesc: false, // and both match your initial from-server sort
...
headers: [
{ text: 'Contract Date', sortable: true, value: 'contractDate' },
{ text: 'Contract Value', sortable: true, value: 'contractValue' }
...
]
},
methods: {
sortTable() {
if(this.sortBy === 'contractDate') {
this.contracts.sort( (a,b) => {
return ( a.contractDate > b.contractDate ? 1 : -1 ) * (this.sortDesc ? -1 : 1 );
});
}
if(this.sortBy === 'contractValue') {
this.contracts.sort( (a,b) => {
return ( a.contractValue > b.contractValue ? 1 : -1 ) * (this.sortDesc ? -1 : 1 );
});
}
}
}
}
Your sort logic obviously will vary, but included the Array.sort() callback example there to show where this.sortDesc is used to reverse the evaluation.
A big caveat to be aware of is that if you do not specify
<v-data-table :must-sort="true" >
Then the bound @update:sort-by and @update:sort-desc events will BOTH fire when sort is deactivated which happens when user clicks the same column header a third time (it cycles through sorted to no-sort states.)
To get around this, add something like
sortPending: false
to your data state, and when the event fires, return immediately out of the event if this.sortPending === true, and move your sort logic into a Vue.nextTick() callback:
methods: {
sortTable() {
if(this.sortPending) return;
this.sortPending = true;
this.$nextTick( () => {
this.sortPending = false;
/* your sort logic here */
});
In data table add attribute:
:sort-desc.sync="sort_desc"
In data:
sort_desc:true,//or what you need by default
In observable:
sort_desc:function(val,_prev){
//do what you need to change sort and refresh
},
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With