Filtering the list

We did a lot of work in laying a foundation for the app in the last step, so now we’ll do something simple: we will add full text search (yes, it will be simple!).

Modifying beer-list template

We use Twitter Bootstrap column model to divide the page in two (fully responsive) columns, the left one for the search box, the right one for the beer list.

We need to add a standard HTML <input> tag, give them some magical data-binding properties and adding a filtering function to the v-for to show only the searched results.

This lets a user enter search criteria and immediately see the effects of their search on the beer list.

Let’s begin by modifying the template to add the search input.

<div class="container">
    <div class="row">
        <div class="col-md-3">
        <!--Sidebar content-->
        <div>Search:</div> 
        <div><input id="searchInput"></div>
        </div>
        <div class="col-md-9">
            <div class="beers">
                <div class="beer" v-for="beer in beers">
                    <beer-list-item 
                        v-bind:name='beer.name'
                        v-bind:description='beer.description'>
                    </beer-list-item>
                </div>
            </div>
        </div>
    </div>
</div>  

Two-ways data-binding

Now we need to link the search input field value to a property of the object.

First we begin by declaring filterText as a internal state variable for the component, i.e. we put it in the data object:

data: function() {
    return { 
        filterText: '',   
        [...]
    };
}

In the template we use value to link the input event of the input item to the filterText data property, and we add a label under it to show the current value of filterText:


<div class="col-md-3">
    <!--Sidebar content-->
    <div>Search:</div> 
    <div>
        <input id="searchInput" v-model="filterText">
    </div>    
    <div>
        <div>Current search:</div>
        <div>{{filterText}}</div>
    </div>
</div>

when testing that you will learn that Vue.js templates need to have a single main tag, not two, so you will need to rewrite your whole template:

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div>Search:</div> 
            <div>
                <input id="searchInput" v-model="filterText">
            </div>
            <div>
                <div>Current search:</div>
                <div></div>
            </div>
        </div>
        <div class="col-md-9">
            <div class="beers">
                <div class="beer" 
                        v-for="beer in beers">
                    <beer-list-item 
                        v-bind:name='beer.name'
                        v-bind:description='beer.description'>
                    </beer-list-item>
                </div>
            </div>
        </div>
    </div>
</div> 

And now we have a two-way data-binding between the input field and the label under it.

{{ message }}

Adding a filter to the list

Now we want to use that filterText property to filter the list.

In order to do it we can use a filteredText depending on filterText. The methods of a Vue component are called everytime the depending properties change.

So we add a filteredText object to our beer-list component options:

methods: {
    filteredList: function (beers,filterText) {
        if (!filterText) {
            return  [ ... beers];;
        }
        return beers.filter( (beer) => {
            return beer.name.match(new RegExp(filterText, 'i'));
        })
    }
},

And in the v-for directive, we iterate on the filtered list:

<div class="beer" v-for="beer in filteredList(beers, filterText)">
    <beer-list-item 
        v-bind:name='beer.name'
        v-bind:description='beer.description'>
    </beer-list-item>
</div>

And now we have a working filter for our beers!

{{ message }}