Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VueJs - Table pagination and filter

I'm using Vue2.js and Element UI as a framework. I would like to be able to filter a table which is sliced. To do so, I use the table and filter components whose documentation could be found here.

Situation OK

The table is not sliced. When you picked a filter, a loop goes trough each row and check if the value of the column is equal to the filter.

Situation NOT OK

The table is sliced. When you picked a filter, a loop goes trough each row that results of the slice and check if the value of the column is equal to the filter. By doing that we don't filter the "hidden" values.

I've made a little https://jsfiddle.net/acm3q6q8/3/ so it is easier to understand.

All of this make sense since I'm not working on the whole data, but on a sliced version.

One solution could be to hide rows instead of excluding them by slicing the data, but I'm wondering if there is a better solution ?

What I want to achieve

  • In the jsfiddle, display only 2 items.
  • Filter the tag to display only rows whose tag is Office

Actual result

There is no row displayed since the row whose tag was office was not part of the sliced table.

Expected result

When filtering, I would like to take into account rows that are not necessarily displayed.

Important

This should work fine with a multiple filter (ie I select several tags)

EDIT

In the same extent if you want to sort the name by alphabetical order, Albert won't be displayed if you displayed only 2 items.

like image 527
Léo Coco Avatar asked Apr 17 '17 18:04

Léo Coco


2 Answers

You can handle the filter-change event on the table component (documented here), and filter/slice yourself.

var Main = {
    data() {
      return {
        numberItemToShow : 4,
        tableData: [...],
        filter: []
      }

    },
    computed : {
      filterData() {
          if (!this.filter.length)
            return this.tableData.slice(0, this.numberItemToShow)
          else
            return this.tableData
              .filter(row => this.filter.includes(row.tag))
              .slice(0, this.numberItemToShow);
      }
    },
    methods: {
      onFilterChange(filters){
        if (filters.tag)
            this.filter = filters.tag;
      }
    }  
}

And the template

<template>
<input v-model="numberItemToShow" placeholder="edit me">
<p>Number of item to display: {{ numberItemToShow }}</p>
  <el-table ref="tab" :data="filterData" border style="width: 100%" @filter-change="onFilterChange">
    <el-table-column prop="name" label="Name"   sortable>
    </el-table-column>
    <el-table-column prop="tag" label="Tag" column-key="tag" :filters="[{ text: 'Home', value: 'Home' }, { text: 'Office', value: 'Office' }]">
      <template scope="scope">
        <el-tag :type="scope.row.tag === 'Home' ? 'primary' : 'success'" close-transition>{{scope.row.tag}}</el-tag>
      </template>
    </el-table-column>
  </el-table>
</template>

Example.

like image 129
Bert Avatar answered Oct 15 '22 11:10

Bert


The problem is that the slicing is done before the filtering. The filter has to see the original data, and the row-counting must be part of the filtering.

Since the filter looks at one row at a time, keeping track of the matched rows is a little tricky. What I did here is keep a counter of matched rows that resets to zero when the row being looked at is the first row of data. This is hacky, but it works. There may be a better way; I am not familiar with the table widget.

var Main = {
    data() {
      return {
      	numberItemToShow : 4,
        tableData: [{
          name: 'One',
          tag: 'Home'
        }, {
          name: 'Two',
          tag: 'Home'
        }, {
          name: 'Three',
          tag: 'Home'
        }, {
          name: 'Four',
          tag: 'Office'
        }],
        scratchCounter: 0
      }
    },
    methods: {
      filterTag(value, row) {
        const matched = row.tag === value;

        if (row === this.tableData[0]) {
        	this.scratchCounter = 0;
        }
        if (matched) {
        	++this.scratchCounter;
        }
        return this.scratchCounter <= this.numberItemToShow && matched;
      }
    }
  }
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
@import url("//unpkg.com/element-ui/lib/theme-default/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<template>
<input v-model="numberItemToShow" placeholder="edit me">
<p>Number of item to display: {{ numberItemToShow }}</p>
  <el-table :data="tableData" border style="width: 100%">
    <el-table-column prop="name" label="Name">
    </el-table-column>
    <el-table-column prop="tag" label="Tag" :filters="[{ text: 'Home', value: 'Home' }, { text: 'Office', value: 'Office' }]" :filter-method="filterTag">
      <template scope="scope">
        <el-tag :type="scope.row.tag === 'Home' ? 'primary' : 'success'" close-transition>{{scope.row.tag}}</el-tag>
      </template>
    </el-table-column>
  </el-table>
</template>
</div>
like image 33
Roy J Avatar answered Oct 15 '22 10:10

Roy J