Reputation: 1469
I've been working with vue js 1.27 on a project and I need to be able to sort a list by numeric values, Alphabetic and reverse order. I've been trying this all day with not really much progress.
I've left some of my previous code in my code sample to let you know what I've already attempted.
{% extends 'base.html.twig' %}
{% block body %}
<div id="wrap">
<h2>Select a category</h2>
<ul id="categorySelect">
<li v-for="cat in categories | orderBy reverse" @click="selectCategory(cat)" class="${cat.selectedCategory == category ? 'selected' : ''}">${cat.title}</li>
</ul>
</div>
{% endblock %}
{% block javascripts %}
<script type="text/javascript">
Vue.config.delimiters = ['${', '}'];
new Vue({
el: '#wrap',
data: {
//reverse: -1,
wasClicked: true,
selectedCategory: null,
categories: [{
title: 'ALL',
category: null
},
{
title: 'CATE',
category: 'sport'
},
{
title: 'DOG',
category: 'sport'
},
{
title: 'SPEED',
category: 'sport'
},
{
title: 'CAT',
category: 'sport'
},
{
title: 'SPORT',
category: 'sport'
},
{
title: 'ART',
category: 'sport'
},
{
title: 'PEOPLE',
category: 'people'
},
{
title: 'CAR',
category: 'car'
}]
},
filters: {
categoryFilter: function (infoBlocs) {
return this.wasClicked ? this.categories : {};
},
caseFilter: function () {
if (this.wasClicked) {
return this.reverseArray();
}
return this.alphaSortByKey(this.categories, 'category');
},
reverse: function(value) {
// slice to make a copy of array, then reverse the copy
return value.slice().reverse();
}
},
methods: {
selectCategory: function(category) {
//this.wasClicked =! this.wasClicked;
//this.categories = this.alphaSortByKey(this.categories, 'category');
// if (this.reverse) {
// this.categories = this.alphaSortByKey(this.categories, 'category');
// }
// else {
// this.categories = this.reverseArray();
// }
if (this.reverse) {
this.categories = this.alphaSortByKey(this.categories, 'category');
this.reverse = false;
}
else {
this.categories = this.reverseArray();
//this.reverse = true;
}
},
alphaSortByKey: function (arr, key) {
arr.sort(function (a, b) {
if (a[key] < b[key])
return -1;
if (a[key] > b[key])
return 1;
return 0;
});
return arr;
},
reverseArray: function () {
return this.categories.reverse();
},
changeOrder: function (event) {
var self = this;
self.reverse = self.reverse * -1
var newItems = self.categories.slice().sort(function (a, b) {
var result;
if (a.name < b.name) {
result = 1
}
else if (a.name > b.name) {
result = -1
}
else {
result = 0
}
return result * self.reverse
})
newItems.forEach(function (item, index) {
item.position = index;
});
this.categories = newItems;
}
}
});
</script>
{% endblock %}
Upvotes: 4
Views: 11415
Reputation: 19016
There is my List Component implementation with client-side order implementation:
<template>
<div>
<table class="table table-bordered" v-if="data.length">
<thead>
<tr>
<th v-for="(colValue, colKey) in cols" :key="colKey">
<a @click="sort(colKey)" href="javascript:void(0)">
{{colValue}}
<icon :name="(sortColumn === colKey) ? (sortAsc ? 'sort-down' : 'sort-up') : 'sort'"></icon>
</a>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in data" :key="row.id">
<td v-for="(colValue, colKey) in cols" :key="row.id + colKey">{{row[colKey]}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import _ from 'lodash';
import apiServer from '@/utils/apiServer'; //
export default {
name: 'List',
data() {
return {
data: [],
sortColumn: '',
sortAsc: true
};
},
props: {
cols: {
type: Object,
required: true
},
apiEndpoint: {
type: String,
required: true
}
}
created() {
this.fetchData();
},
watch: {
'$route': 'fetchData'
},
methods: {
async fetchData() {
const response = await apiServer.get(this.apiEndpoint);
this.data = response.data;
},
sort(colKey) {
this.data = _.sortBy(this.data, [colKey]);
if (this.sortColumn === colKey) {
if (!this.sortAsc) {
this.data = _.reverse(this.data);
}
this.sortAsc = !this.sortAsc;
} else {
this.sortAsc = false;
}
this.sortColumn = colKey;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
<template>
<div>
<h1>Orders</h1>
<List :cols="cols" api-endpoint="/orders" title="Orders" />
</div>
</template>
<script>
import List from '@/components/List.vue';
export default {
name: 'OrderList',
components: { List },
data() {
return {
cols: {
id: 'Id',
title: 'Title',
created_at: 'Created at'
}
};
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
Upvotes: 0
Reputation: 1217
Here is a fiddle with working functionality to sort and reverse the order of your array. For reverse I just used the built in reverse() Javascript function. For the alphanumeric sort I borrowed the solution from this answer: https://stackoverflow.com/a/4340339/6913895
https://jsfiddle.net/n1tbmgo9/
Html:
<div id="wrap">
<h2>Select a category</h2>
<button @click="sort">
Sort alphanumeric
</button>
<button @click="reverse">
Reverse list
</button>
<ul id="categorySelect">
<li v-for="cat in categories">${cat.title}</li>
</ul>
</div>
Javascript:
Vue.config.delimiters = ['${', '}'];
new Vue({
el: '#wrap',
data: {
selectedCategory: null,
categories: [{
title: 'ALL',
category: null
},
{
title: 'CATE',
category: 'sport'
},
{
title: 'DOG',
category: 'sport'
},
{
title: 'SPEED',
category: 'sport'
},
{
title: 'CAT',
category: 'sport'
},
{
title: 'SPORT',
category: 'sport'
},
{
title: 'ART',
category: 'sport'
},
{
title: 'PEOPLE',
category: 'people'
},
{
title: 'CAR',
category: 'car'
}]
},
methods: {
sort: function () {
this.categories.sort(this.sortAlphaNum);
},
reverse: function () {
this.categories.reverse();
},
sortAlphaNum: function (a,b) {
var reA = /[^a-zA-Z]/g;
var reN = /[^0-9]/g;
var aA = a.title.replace(reA, "");
var bA = b.title.replace(reA, "");
if(aA === bA) {
var aN = parseInt(a.title.replace(reN, ""), 10);
var bN = parseInt(b.title.replace(reN, ""), 10);
return aN === bN ? 0 : aN > bN ? 1 : -1;
} else {
return aA > bA ? 1 : -1;
}
}
}
});
The built in reverse()
function is straightforward so I will elaborate on the sortAlphaNum()
sort function. That function is passed into the sort()
function and must return either 1, 0, or -1 to indicate if the objects passed in should be moved in a particular direction in the array.
The variables reA
and reN
are regexes to identify alphabetic and numeric characters, respectively.
First the function removes all alphabet characters from the titles of the two objects passed in and compares them for equality.
var aA = a.title.replace(reA, "");
and
var bA = b.title.replace(reA, "");
If they are not equal then it means we have alphabet characters (as opposed to just numeric input) and we can sort them accordingly.
return aA > bA ? 1 : -1;
If the titles with alphabet characters stripped are equal (if(aA === bA)
) then we remove numeric digits from the object titles (leaving non-numeric characters).
var aN = parseInt(a.title.replace(reN, ""), 10);
var bN = parseInt(b.title.replace(reN, ""), 10);
Then we compare the resulting variables and return the appropriate sorting value (1, 0, -1).
return aN === bN ? 0 : aN > bN ? 1 : -1;
Upvotes: 4
Reputation: 67
create a computed property where you manually sort it and then loop for that instead the data prop
Upvotes: 0