Switching Between Tables With Vue Good Table

Posted by on January 14, 2022 · 12 mins read

Building Data Tables

A common problem for developers to solve is the question of how to best display data once collected. There are a variety of different ways to accomplish this goal, but one tool that I have found of significant value is Vue Good Table. This tool comes stocked full of a variety of tools that make displaying your data to users easier than waving a wand. In one particular instance I found myself working with a Rails backend and a Vue front end that uses slim and I needed to display a variety of user data.

The documentation provides excellent examples of how to properly set up a basic table, but we needed something more. What we needed was the ability to display two different tables without the time required to load a new set of data. The trigger for this change of tables was to click one of two buttons on the page that would indicate which of the tables was being viewed. Easy enough, right? Not as much as I was hoping. The initial step was to set up and configure the first table. This follows exactly as the documentation describes.

     columnsForUsers: [
       {
         label: 'User Name',
         field: 'name',
         tdClass: 'name-td',
       },
       {
         label: User Data Column 1',
         field: userData1,
         tdClass: 'user-one-td',
       },
       {
         label: 'User Data Column 2',
         field: 'userData2',
         tdClass: 'user-two-td',
       },
     ],

Configuring the Backend and Slim Files

The above code was used to define the column headers for the table. Because these values are static they could be defined statically. With the column definitions defined the next step was to configure the data that was displayed in each of the rows. This process was done in the Rails controller and took the following form:

users = model.model_method.where(data: "Value").includes(:alternate_model)
   @userData = users.map do |user|
     {
       name: user.full_name,
       userData1: user.data1,
       userData2: user.data2
     }
   end

This provided an instance variable that could be passed to the Vue instance and rendered in the table. With the column headers defined and the row data being sent from the controller there was only one thing left to do, build the table.

vue-good-table :columns="columnsForUsers" :rows="userData" :search-options="{enabled: true, placeholder: ''}"

With this the table was working exactly like we needed.

Making Sorting Work

The next obstacle to tackle was the ability to sort through the values in each row. Conveniently this functionality is built into Vue Good Table, so there wasn’t anything that we needed to do, except for one problem. Some of our row data contained links. Searching worked just fine, but we found that sorting when there were links in the table caused a problem. Each link started with an <a> tag with a variety of different options being passed into each tag. This caused the sorting to make no sense from a user perspective. To fix this we simply had to override the default sort function.

sortFn(x, y) {
     x = x.replace(/<\/?a[^>]*>/g, '')
     y = y.replace(/<\/?a[^>]*>/g, '')

     return x < y ? -1 : x > y ? 1 : 0
   }

Using regular expressions we were able to remove the tags from consideration when the table applied the sort function which corrected our problem and resulted in the rows being sorted by the contents of the links instead of by the link tags themselves. This new function then needed to be passed into the table upon creation.

vue-good-table :columns="columnsForObjectives" :rows="mastery_objective_rows" :search-options="{enabled: true, placeholder: ''}" :sort-options="{ enabled: true, multipleColumns: true, initialSortBy: [] }"

The sort function that we defined used the same name as the default sort function, so our new definition effectively overrode the default sort, so we didn’t have to specify a new sort function when we defined the table. Now we had our first table working exactly as we needed, so the next step was to get our second table up and running.

Making the Second Table

The process for the second table worked exactly the same as the first. Once we had it constructed and the needed information in the controller and got the second table defined we needed to add a few buttons that would switch between the tables. We used two buttons and had the button for the currently active table change colors to indicate to the user which table was being displayed. While we thought about using a ternary to change the values that were passed to the table when the button was clicked, we ended up deciding to keep two different tables there that would only be created depending on which button was pressed to help the code stay more readable.

div.change-report-data.toggle
   input.report-type name='type' checked='checked' id='user1Radio' type='radio' v-on:click='this.toggleUser1Table'
   label for='user1Radio' = "By User Data 1"
   input.report-type name='type' id='user2Radio' type='radio' v-on:click='this.toggleUser2Table'
   label for='user2Radio' = "By User Data 2"

 div v-if="showUser1Table"
   vue-good-table :columns="columnsForUser1" :rows="user1_rows" :search-options="{enabled: true, placeholder: ''}" :sort-options="{ enabled: true, multipleColumns: true, initialSortBy: [] }"
 div v-if="!showUser1Table"
   vue-good-table :columns="columnsForUser2" :rows="user2_rows" :search-options="{enabled: true, placeholder: ''}" :sort-options="{ enabled: true, multipleColumns: true, initialSortBy: [] }"

This way we had a single boolean value that controlled which table would be displayed. The function that was triggered to change the tables then took the following form:

   toggleUser1Table() {
     this.showUser1Table = true
   },
   toggleUser2Table() {
     this.showUser1Table = false
   },

Now we have everything that we need and the tables work correctly, right? Not quite.

The Trouble With Sorted Tables

When we started testing to verify that everything was working correctly we encountered an error.

[Vue warn]: Error in getter for watcher "selectedRows": "TypeError: Cannot read properties of undefined (reading 'sortFn')"

The error occurred any time you tried to sort a table and then switch to the other table. The problem is that when you sort a table the sort priority is stored in the initialSortBy array. When we changed tables that value was still stored but the column that Vue Good Table was trying to sort with no longer existed. To fix this problem we had to clear that value when the button to change tables was pressed. This changed our table functions to look like this:

toggleUser1Table() {
     this.$children[0].sorts = []
     this.showUser1Table = true
   },
   toggleUser2Table() {
     this.$children[0].sorts = []
     this.showUser1Table = false
   }

Now when you sort a table and try to switch to the other table, Vue Good Tables doesn’t try to apply an initial sort and everything works the way it should.

The problem of displaying data to users has many different solutions and can take on different forms depending upon the needs of the client. This particular issue took a little bit of digging so hopefully it will be helpful to others in the future who find themselves in a similar situation.

Resources

Vue Good Table was the tool we used to make the datatable

We at Radial Development Group worked on the feature

We used Vue for our front end

Ruby on Rails was our backend of choice for this project

Slim is a great tool to streamline your erb work

Regex for cleaning up html tags