mrkaluzny homepage
Tips & Tricks

How to create sortable and filterable table with ElementUI and Laravue?

Dec 17, 2019

Recently I had to create a backend admin view for data obtained from the iOS app. With most of the client’s budget allocated towards the iOS application development(duh) we had to come up with a quick way to set up the backend for the project.

Dashboard? Fortunately there’s a quick way to set it up with Laravue & ElementUI

I’ve decided to use my go-to tools for quick smaller projects - Vue & Laravel. Since dashboards are mostly uniform, and end-users won’t see them anyway, it was a no-brainer to use something with prebuild functionality. I’ve decided to go with Laravue although it uses Element UI which I haven’t tried so far.

Laravue is really awesome and you should definitely consider it for your next project. It has lots of features working out of the box, such as authentication, user-roles and much more. All I needed to get my project up & running was to generate 2 database migrations, write a couple of controllers and modify existing views to fulfill the requirements.

Pagination and sorting?! Sorry, that’s just impossible.

To display data I used the table component.

That’s when the trouble with ElementUI started. I needed to use pagination (scrolling through thousands of entries would be highly inefficient and unpleasant). Additionally, the client needed a way to filter the results based on each column values (location, date of entry, selected value from a range of options, etc.).

I began to use dev’s best friend - Google to quickly find a solution. I was astounded when it turned out there’s no quick fix for my case.

Well, no worries! I’ve decided to write it myself (I’ve also posted the snippets in the issue on GitHub, check it out for more context).

Data object declared in Vue component:

 data() {
    return {
      unfiltered: '', // Stores raw data for the table
      list: '', // Stores filtered table data
	    filters: {}, // Stores active filters with values
      chunkedList: [],
      total: 0,
      listQuery: {
        page: 1, // Tracks current page
        limit: 20, // Sets limit of items per page
      },
    };
  },

Here’s the code depicting methods used in my Vue component with comments.

	// This method chunks list into pages
	chunkList(list) {
      const result = []
      var i, j, temparray, chunk = this.listQuery.limit;
      for (i = 0, j = list.length; i < j; i += chunk) {
        temparray = list.slice(i, i + chunk);
        result.push(temparray)
      }
      this.chunkedList = result
      this.listQuery.page = 1 // Defaults to show first chunk
    },

	// handleFilterChange should be attached to @filter-change event on table element (remember to add column-key for table-column)
    handleFilterChange(e) {
      const propertyName = Object.getOwnPropertyNames(e)[0]
      const value = e[propertyName]
      this.filters[propertyName] = value
      this.filterAndChunkList()
    },

    // This method is responsible for filtering keys before chunking them into pages
    filterAndChunkList() {
      const filters = this.filters
      const list = this.unfiltered
      var filteredList = []
      let areFiltersActive = false
      var i = 0
      Object.keys(filters).forEach((key) => {
        const filterValues = filters[key]
        let filteredRows

        if (filterValues.length !== 0) {
          areFiltersActive = true
          filterValues.forEach((value, y) => {
            const listToFilter = i === 0 ? list : filteredList
            filteredRows = listToFilter.filter(row => {
                return row[key] === value
                // Change if any keys require different filters
            })

            if (i !== 0 && y === 0) {
              filteredList = filteredRows
            } else {
              filteredList.push(...filteredRows)
            }
          })
          i++
        }
      })

      const result = areFiltersActive ? filteredList : list

      this.list = result
      this.chunkList(result)
    },

And finally, the code to bind it all together with Table component in Vue template.

<el-table
  v-loading="listLoading"
  :data="chunkedList[listQuery.page - 1]"
  border
  fit
  highlight-current-row
  style="width: 100%"
  @selection-change="handleSelectionChange"
  @filter-change="handleFilterChange"
>
  <el-table-column type="selection" align="center" />
  <el-table-column align="center" label="Id" width="65">
    <template slot-scope="scope"> {{ scope.row.id }} </template>
  </el-table-column>
  <el-table-column label="Full Name" sort-by="first_name" sortable width="235">
    <template slot-scope="scope">
      {{ scope.row.first_name }} {{ scope.row.last_name }}
    </template>
  </el-table-column>
  ...
</el-table>

<div style="padding: 10px" />
<el-pagination
  :page-size="listQuery.limit"
  :hide-on-single-page="true"
  :pager-count="11"
  layout="total, sizes, prev, pager, next, jumper"
  :page-sizes="[20, 50, 100, 150]"
  :total="list.length"
  @current-change="handlePageChange"
  @size-change="handleSizeChange"
/>

I hope this one helps you out a bit in working with ElementUI and Laravue!