Reducing the number of filter variables and grouping logically
The filtering currently uses up three variables, filterField, filterQuery, and filterUserState. The only thing that currently links these variables is the name, rather than being in an object of their own to link them systematically. Doing this avoids any ambiguity as to whether they are related to the same component or just coincidentally the same. In the data object, create a new object titled filter and move each variable inside:
To access the data, update any references of filterField to this.filter.field. Note the extra dot, denoting it is a key of the filter object. Don't forget to update filterQuery and filterUserState references as well. For example, the isActiveFilterSelected method would become:
You will also need to update the v-model and v-show attributes in your view—there are five occurrences of the various variables.
While updating the filtering variables, we can take this opportunity to remove one. With our current filtering, we can only have one filter active at a time. This means the query and userState variables are only being used at any one time, which gives us the opportunity to combine these two variables. To do so, we'll need to update the view and application code to cater for this.
Remove the userState variable from your filter data object and update any occurrence of filter.userState in your view to filter.query. Now do a find and replace in your Vue JavaScript code for filter.userState, again replacing it with filter.query.
Viewing your app in the browser, it will appear to initially work, being able to filter users by the field. However, if you filter by status, then switch to any other field, the query field won't show. This is because using the radio buttons sets the value to a Boolean which, when trying to convert to lowercase for the query field, fails to do so. To tackle this, we can convert whatever value is in the filter.query variable to a string using the native JavaScript String() function. This ensures that our filtering function can work with any filtering input:
if(this.filter.field === 'isActive') { result = (typeof this.filter.query === 'boolean') ? (this.filter.query === person.isActive) : true; } else { let query = String(this.filter.query), field = person[this.filter.field]; if(typeof field === 'number') { query.replace(this.currency, ''); try { result = eval(field + query); } catch(e) {} } else { field = field.toLowerCase(); result = field.includes(query.toLowerCase()); }
Adding this to our code now ensures our query data is usable no matter what the value. The issue this now creates is when the user is switching between fields to filter. If you select the Active user and chose a radio button, the filtering works as expected, however, if you now switch to Email, or another field, the input box is prepopulated with either true or false. This instantly filters and will often return no results. This also occurs when switching between two text filtering fields, which is not the desired effect.
What we want is, whenever the select box is updated, the filter query should clear. Whether it is the radio buttons or input box, selecting a new field should reset the filter query, this ensures a new search can begin.
This is done by removing the link between the select box and the filter.field variable and creating our own method to handle the update. We then trigger the method when the select box is changed. This method will then clear the query variable and set the field variable to the select box value.
Remove the v-model attribute on the select box and add a new v-on:change attribute. We will pass a method name into this that will fire every time the select box is updated.
v-on is a new Vue binding that we've not encountered before. It allows you to bind actions from elements to Vue methods. For example, v-on:click is one that is used the most commonly - which allows you to bind a click function to the element. We'll cover more on this in the next section of the book.
Where v-bind can be abbreviated to just a colon, v-on can be shortened to an @ symbol, allowing you to use @click="", for example:
This attribute is firing the changeFilter method on every update and passing it the $event data of the change. This default Vue event object contains a lot of information that we could utilize, but the target.value data is the key we are after.
Create a new method in your Vue instance that accepts the event parameter and updates both the query and field variables. The query variable needs to be cleared, so set it to an empty string, whereas the field variable can be set to the value of the select box: