6

My table is based on the Grid Component Example in Vue.js' website

I'm having problem with sorting dates inside the table. I get all the table data from server side as JSON. So in the codes provided, I just mocked the data in var mockDataFromServerSide.

Here is the code: https://jsfiddle.net/5w1wzhvw/3/

HTML file:

<!-- component template -->
<script type="text/x-template" id="grid-template">
  <table>
    <thead>
      <tr>
        <th v-for="key in columns"
          v-on:click="sortBy(key)"
          :class="{active: sortKey == key}">
          {{key | capitalize}}
          <span class="arrow"
            :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
          </span>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="
        entry in data
        | filterBy filterKey
        | orderBy sortKey sortOrders[sortKey]">
        <td v-for="key in columns">
          {{entry[key]}}
        </td>
      </tr>
    </tbody>
  </table>
</script>

<!-- demo root element -->
<div id="demo">
  <form id="search">
    Search <input name="query" v-model="searchQuery">
  </form>
  <demo-grid
    :filter-key="searchQuery">
  </demo-grid>
</div>

Js file:

var gridColumns = ['name', 'date'];
var mockDataFromServerSide = [
      { name: 'Chuck Norris', date: "01 Dec 2016" },
      { name: 'Bruce Lee', date: "23 Apr 2005" },
      { name: 'Jackie C', date: "30 Jan 2012" },
      { name: 'Jet Li', date: "20 Apr 2006" }
    ];
// register the grid component
Vue.component('demo-grid', {
  template: '#grid-template',
  props: {
        filterKey: String
  },
  data: function () {
    var sortOrders = {}
    gridColumns.forEach(function (key) {
      sortOrders[key] = 1
    })
    return {
      sortKey: '',
      sortOrders: sortOrders,
      columns: gridColumns,
      data: mockDataFromServerSide
    }
  },
  methods: {
    sortBy: function (key) {
      this.sortKey = key
      this.sortOrders[key] = this.sortOrders[key] * -1
    }
  }
})

// bootstrap the demo
var demo = new Vue({
  el: '#demo',
  data: {
    searchQuery: ''
  }
})

I also tried to add a filter to the date. The sort is correct but the displayed dates are shown as "Thu Apr 02 2016 00:00:00 GMT+0800 (China Standard Time)". I want the dates to be displayed as 02 Apr 2016.

Added filter Code: https://jsfiddle.net/kr1m5de5/1/

HTML file (added filter):

<!-- component template -->
<script type="text/x-template" id="grid-template">
  <table>
    <thead>
      <tr>
        <th v-for="key in columns"
          v-on:click="sortBy(key)"
          :class="{active: sortKey == key}">
          {{key | capitalize}}
          <span class="arrow"
            :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
          </span>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="
        entry in data
        | filterBy filterKey
        | orderBy sortKey sortOrders[sortKey]
        | datesFilter">
        <td v-for="key in columns">
          {{entry[key]}}
        </td>
      </tr>
    </tbody>
  </table>
</script>

<!-- demo root element -->
<div id="demo">
  <form id="search">
    Search <input name="query" v-model="searchQuery">
  </form>
  <demo-grid
    :filter-key="searchQuery">
  </demo-grid>
</div>

JS file (added filter):

var gridColumns = ['name', 'date'];

var mockDataFromServerSide = [
      { name: 'Chuck Norris', date: "01 Dec 2016" },
      { name: 'Bruce Lee', date: "23 Apr 2005" },
      { name: 'Jackie C', date: "30 Jan 2012" },
      { name: 'Jet Li', date: "20 Apr 2006" }
    ];
// register the grid component
Vue.component('demo-grid', {
  template: '#grid-template',
  props: {
        filterKey: String
  },
  filters: {
    datesFilter: function (data) {
      data.forEach(function (row) {
        row.date = new Date(row.date);
      });
      return data;
        }
    },
  data: function () {
    var sortOrders = {}
    gridColumns.forEach(function (key) {
      sortOrders[key] = 1
    })
    return {
      sortKey: '',
      sortOrders: sortOrders,
      columns: gridColumns,
      data: mockDataFromServerSide
    }
  },
  methods: {
    sortBy: function (key) {
      this.sortKey = key
      this.sortOrders[key] = this.sortOrders[key] * -1
    }
  }
})

// bootstrap the demo
var demo = new Vue({
  el: '#demo',
  data: {
    searchQuery: ''
  }
})

Please let me know how to fix it or if there is a better way to do it.

2
  • I was thinking about adding as special sortFunction for the date column and use either attributes or a hidden column inside mockDataFromServerSide but i could not solve it yet. Commented Apr 4, 2016 at 20:46
  • im so embarrassed i used your jsfiddle as a starting point for what i just answered, like a year and a half ago... i did not even had a stackoverflow account back then, you probably don't even need this anymore :'( Commented Aug 23, 2018 at 5:27

1 Answer 1

0

I solved this by making a TableHeader component it says semantic cause i used semantic-ui... sorry for the spanglish in the code, must of 'em are cognates anyway. Also, this code is working, but if you see improvements to the code/answer let me know please!

As you can see, i really don't sort at front... i make a new request with the sorted items.

<template>
    <th @click="cycleSort(sth, $event)">
        <span><span>{{ sth.texto }}&nbsp;&nbsp;</span><i class="icon" :class="sth.icon"></i><sub v-if="sth.posicion > 0"><small>{{ sth.posicion }}</small></sub></span>
    </th>
</template>
<script>
export default {
  name: "SemanticTableHeader",
  props: {
      sth : {
          type : Object,
          default: () => {}
      },
      sths : {
          type : Array,
          default: () => { return [] }
      },
      filtrosOrder : {
          type : Array,
          default: () => { return [] }
      },
      isSearching : {
          type : Boolean,
          required : true
      }
  },
  methods: {
      cycleSort(sth, event) {

          if(this.isSearching == true){
              return false;
          }

          switch (sth.direction) {
              case null:
              sth.direction = 'asc';
              sth.icon = 'sort ascending';
              break;

              case 'asc':
              sth.direction = 'desc';
              sth.icon = 'sort descending';
              break;

              case 'desc':
              sth.direction = null;
              sth.icon = 'sort disabled';
              break;

              default:
              sth.direction = null;
              sth.icon = 'sort disabled';
          }

        this.manejaCambioHeader(sth);

      },
      manejaCambioHeader: _.debounce(function (sth) {
          var self = this;

          console.log(this.filtrosOrder);

          let auxUser = _.find(this.filtrosOrder, function(o) { return o.id == sth.id; });

          if( auxUser != null ){

              auxUser.direction = sth.direction;

              if(auxUser.direction == null){
                  for (var i=0 ; i < this.filtrosOrder.length ; i++){
                      if (this.filtrosOrder[i].id === auxUser.id) {

                          let auxSths =  _.find(self.sths, function(o) { return o.id == sth.id; });
                          auxSths.posicion = 0;

                          this.filtrosOrder.splice(i, 1);
                      }
                  }
              }
          }else{
              this.filtrosOrder.push({ id: sth.id, direction: sth.direction });
          }

          for (var i=0 ; i < self.filtrosOrder.length; i++){
              let auxSths =  _.find(this.sths, function(o) { return o.id == self.filtrosOrder[i].id; });

              auxSths.posicion = i + 1;
          }

          console.log(this.filtrosOrder);

          this.$emit('sortHeaderChanged', sth);

    }, 400),
  },
}
</script>
<style lang="css" scoped>
th span{
    cursor: pointer !important;
    -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
    -khtml-user-select: none; /* Konqueror HTML */
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* Internet Explorer/Edge */
    user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
}

i.icon{
    margin: 0em -0.2em 0em 0em;
}
</style>

In my Index views i just load the component and use it like this

<template>
    <table>
        <thead>
            <tr>
                <semantic-table-header v-for="sth in sths" :key="sth.key"
                :sth="sth"
                :sths="sths"
                :isSearching="isSearching"
                :filtrosOrder="filtros.orderBy"
                @sortHeaderChanged="fetchIndex"
                ></semantic-table-header>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="contact in contacts" :key="contact.key" :class="[contact.justAdded ? 'justAdded' : '']">
            </tr>
        </tbody>
    </table>
</template>

export default {
    name: "ContactsIndex",
    data:() => ({
        filtros:{
            orderBy:[
                {   id: 'nombre',   direction: 'asc'    } // orderBy is calculated through the headers component
            ]
        },
        sths:[
            {   id: 'nombre',       texto: 'Nombre',                icon: 'sort ascending',     direction: 'asc',   posicion: 1 },
            {   id: 'telefonos',    texto: 'Teléfono(s)',           icon: 'sort disabled',      direction: null,    posicion: 0 },
            {   id: 'emails',       texto: 'Correo Electrónico(s)', icon: 'sort disabled',      direction: null,    posicion: 0 },
            {   id: 'estatus',      texto: 'Estatus',               icon: 'sort disabled',      direction: null,    posicion: 0 }
        ],
        contacts: [],
    }),
    created() {
        this.fetchIndex();
    },
    methods: {
        resetFilters() {
            // this function is to reset filters and headers
            Object.assign(this.$data.filtros, this.$options.data().filtros);
            this.$data.sths =  this.$options.data().sths;
            this.fetchIndex();
        },
        fetchIndex() {
            let self = this;

            // this is a wrapper i made for an axios post call you can replace it with a normal call
            singleIndexRequest('/api/v1/contacts/index', self).then(response => {
                self.contacts = response.data.contacts;
            });
        },
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.