2

I am trying to sort a table by columns. That when pressing the ID button all the column is ordered from highest to lowest or vice versa, and the same by pressing the other two. How can I do it?

<table id="mi-tabla">
            <thead>
            <tr class="encabezado-derecha" >
              <th>ID</th>
              <th>Nombre de sección</th>
              <th>Potencial (€)</th>
            </tr>
            </thead>
            <tbody>
            <tr class="item" v-for="user in userInfo" @click="openDiv(), showInfo1(user.id_section)">
              <td>{{user.id_section}}</td>
              <td>{{user.desc_section}}</td>
              <div class="acceder">
              <td>{{user.sale_potential | currency}}</td>
              <img src="../../iconos/icon/chevron/[email protected]" alt />
              </div>
            </tr>
            </tbody>
          </table>

{
      "id_store": 4,
      "id_section": 1,
      "desc_section": "MATERIALES DE CONSTRUCCION yeh",
      "id_rule": 1,
      "sale_potential": "69413.5525190617"
    },
    {
      "id_store": 4,
      "id_section": 2,
      "desc_section": "CARPINTERIA Y MADERA",
      "id_rule": 1,
      "sale_potential": "74704.3439572555"
    },
    {
      "id_store": 4,
      "id_section": 3,
      "desc_section": "ELECTR-FONTAN-CALOR",
      "id_rule": 1,
      "sale_potential": "101255.89182774"
    }
    ]

2
  • do you want to make the functionality yourself, or just use an existing component that already does that? Commented Apr 23, 2020 at 15:47
  • I would like to do it myself. Commented Apr 23, 2020 at 15:56

3 Answers 3

8

Here's what it might look like if you want to implement yourself, note that this is very basic functionality and as you start to add additional features, you might see more benefit from using a component that already does it all.

Anyhow, the way you can do it is by using a computed (sortedList) to store a sorted version of the array. Then use another data variable to store which column you want to store by (sortBy), and optionally, you can store a sort direction (sortOrder)

then add a sort method that passes the sort key and updates the sortBy value and/or the sortOrder. When either of these values (or even the source array) changes, the computed will re-sort the array using the sort function.

new Vue({
  el: "#app",
  data: {
    sortBy: "id_section",
    sortOrder: 1,
    userInfo: [
      {
        "id_store": 4,
        "id_section": 1,
        "desc_section": "MATERIALES DE CONSTRUCCION yeh",
        "id_rule": 1,
        "sale_potential": "69413.5525190617"
      },
      {
        "id_store": 4,
        "id_section": 2,
        "desc_section": "CARPINTERIA Y MADERA",
        "id_rule": 1,
        "sale_potential": "74704.3439572555"
      },
      {
        "id_store": 4,
        "id_section": 3,
        "desc_section": "ELECTR-FONTAN-CALOR",
        "id_rule": 1,
        "sale_potential": "101255.89182774"
      }
    ]
  },
  computed: {
    sortedList() {
      return [...this.userInfo]
        .map(i => ({...i, sale_potential:parseFloat(i.sale_potential)}))
        .sort((a,b) => {
          if (a[this.sortBy] >= b[this.sortBy]) {
            return this.sortOrder
          }
        return -this.sortOrder
      })
    }
  },
  methods: {
    sort: function(sortBy){
        if(this.sortBy === sortBy) {
        this.sortOrder = -this.sortOrder;
      } else {
        this.sortBy = sortBy
      }
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  [{{sortBy}}] [{{sortOrder}}]
  <table id="mi-tabla">
    <thead>
      <tr class="encabezado-derecha">
        <th @click='sort("id_section")'>{{ sortBy === 'id_section' ? '*' : '' }}ID</th>
        <th @click='sort("desc_section")'>{{ sortBy === 'desc_section' ? '*' : '' }}Nombre de sección</th>
        <th @click='sort("sale_potential")'>{{ sortBy === 'sale_potential' ? '*' : '' }}Potencial (€)</th>
      </tr>
    </thead>
    <tbody>
      <tr class="item" v-for="user in sortedList">
        <td>{{user.id_section}}</td>
        <td>{{user.desc_section}}</td>
        <div class="acceder">
          <td>{{user.sale_potential | currency}}</td>
          <img src="../../iconos/icon/chevron/[email protected]" alt />
        </div>
      </tr>
    </tbody>
  </table>
</div>
Sign up to request clarification or add additional context in comments.

5 Comments

I removed the @click ion the td, but it remains functional
but does not order alphabetically or the numbers from highest to lowest
right, 🤦 I forgot to include that part. Need to use the key in the sort like this a[this.sortBy] >= b[this.sortBy] updated code.
Now if it works, the only thing is that the third column (potencial) does not order it by number. Thank you!!
that's where we get into the issues of fine-tuning. The reason for that is that the sorting is done on the string value. so the sorting function needs to know, for that column only, to convert to a number. The way I've handled is that I have a table config that includes this sort of information (as well as many additional things like a function that formats each row for given column). But in this case, you could convert it into a numeric value in your sortedList computed before you sort it. I've updated answer with example.
1

I would recommend you to use bootstrap Vue tables which come with filtering and sorting. All you have to do is pass your data to the table.

Here is a link you can check it out.

https://bootstrap-vue.js.org/docs/components/table#complete-example

< script >
  export default {
    data() {
      return {
        items: [{
            "id_store": 4,
            "id_section": 1,
            "desc_section": "MATERIALES DE CONSTRUCCION yeh",
            "id_rule": 1,
            "sale_potential": "69413.5525190617"
          },
          {
            "id_store": 4,
            "id_section": 2,
            "desc_section": "CARPINTERIA Y MADERA",
            "id_rule": 1,
            "sale_potential": "74704.3439572555"
          },
          {
            "id_store": 4,
            "id_section": 3,
            "desc_section": "ELECTR-FONTAN-CALOR",
            "id_rule": 1,
            "sale_potential": "101255.89182774"
          }
        ],
        fields: [{
          key: 'id_store',
          label: 'id',
          sortable: true
        }, {
          key: 'desc_section',
          label: 'Nombre de sección'
        }, {
          key: 'sale_potential'
        },{key:'actions'}]
      }
    },
  } <
  /script>
   <b-table striped hover :items="items" :fields="fields">
    <template v-slot:cell(sale_potential)="row">
           <p>{{row.item.sale_potential |currency}}</p>
         <img src="../../iconos/icon/chevron/[email protected]" alt />
     </template> 
   <template v-slot:cell(actions)="row">
     <button @click="openDiv(); showInfo1(row.item.id_section);" 
         class="btn" variant="primary">Action</button>
    </template> 
  </b-table>

4 Comments

the problem is that I add a filter to sale_potential so that it separates by thousands and adds the money symbol, also in tbody it has a click event that could not be added in the bootstrap way.
For the filter, you can use slots. Let me update the answer. What is the click event supposed to do?
the event does two things, 'openDiv ()' opens a new div where a list is displayed that calls me back the api I do with the second event by picking the id_section 'showInfo1 (user.id_section)'
okay, we can add a button in the row via a slot which when clicked does the above event. Let me add that to the answer. Hope it answers your question.
1

If you want to add this functionality yourself you can achieve it using a computed value to sort your data.

data () => ({
  ...
  sortBy : null,
}),
computed : {
  userInfoSorted () {
    const sortBy = this.sortBy
    if (!sortBy) return this.userInfo
    return this.userInfo.sort((a,b)=> a[sortBy] > b[sortBy] ? 1 : -1)
  }
}

Then update your sortBy value within the <th> tags in your template:

<th @click="sortBy='id_section'">ID</th>

and link your rows to the computed value:

<tr class="item" v-for="user in userInfoSorted">

EDIT: CHANGE SORT ORDER

To add an option to toggle the order, start by adding the headers to your data object:

data () => ({
  ...
  headers : {
    id_section : {
       text : 'ID',
       reverse : true
    }
  }
})

Then update your template to also change the reverse value on click:

  <th v-for="(val,key) in headers" @click="sortBy=key; val.reverse=!val.reverse">
   {{ val.text }}
  </th>

Finally include the reverse value in your sort function:

userInfoSorted () {
    const sortBy = this.sortBy
    const r = this.headers[sortBy].reverse ? -1 : 1;
    if (!sortBy) return this.userInfo
    return this.userInfo.sort((a,b)=> a[sortBy] > b[sortBy] ? 1*r : -1*r)
  }

3 Comments

Thank you!! It works, but how would you do that when pressing a second time it is ordered from highest to lowest
And doesn't alphabetize the second column
I've updated the answer to show how you could toggle the order and changed the sort function so it will work with letters.

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.