A PetiteVue Tutorial - 04 Searching
Tags: javascript, vue, beginners, tutorial
Published: 2023-01-29 19:19:27

Now that we have a table that is being generated dynamically from a server. (Not really but let's pretend.) We can now set up searching for the table.

Now that we are getting a bit more complicated, we'll need to rewire some things so that we can get better error handling.

## The Javascript

The first thing will do is set up our javascript so that we have the variable available to us.

``` js
createApp({
    searchString: "",
    search,
    filteredWorkers: [],

    getWorkers,
    workers: [],
    loaded: false,

    mounted,
}).mount("#app");
```

We have now added a lot more variables to our application.

The first thing we've added is the searchString. This variable will be bounded to an input element. That way when you type something, the variable will be updated immediately as well.

We added the search function so that we can do searching. This is also where the filteredWorkers variable will be used. We don't want to lose our set of workers when searching so we will make a copy of it for when we need to search or display the workers.

The next thing is that we are going to add the getWorkers function to our PetiteVue application. This will give it access to the petitevue internal variables via the `this` keyword. We need this so that getWorkers can update a loaded variable.

We don't want to just check if workers is an empty string, we want to keep track of if the data has been loaded.

Now that we know what variables and functions we need, let's look at the implementation.

``` html
<script>
    import { createApp } from '/js/petite-vue.es.js'

    async function getWorkers() {
        let workers = ...;
        this.loaded = true;
        return workers;
    }

    function search() {
        this.filteredWorkers = this.searchString === ""
            ? this.workers
            : this.workers.filter(wo => Object.values(wo).join("").indexOf(this.searchString) !== -1);
    }

    async function mounted() {
        this.workers = await this.getWorkers();
        this.filteredWorkers = this.workers;
    }

    createApp({
        ...
    }).mount("#app");
</script>
```

We now have a search function that will update the filteredWorkers list. We have also updated our getWorkers function to set the loaded variable to true.

## The HTML

Now let's look at the html, here we are going to see changes in how things are nested. We are also going to be using our loaded variable for displaying messages.

``` html
<div id="app" @vue:mounted="mounted">
    <div v-if="!loaded">Waiting for data...</div>
    <div v-else>
        <input type="text" v-model="searchString">
        <button @click="search">Search</button>
        <table>
            ...
            <tbody>
                <tr v-for="worker in filteredWorkers">
                    ...
                </tr>
            </tbody>
        </table>
    </div>
</div>
```

Here we update our table and embed it inside a div. This way we could get the conditional to wrap both our search bar and our table.

We use the v-model attribute to bind the variable searchString to the one we have in our table.

We have also added a button and added an click event. This event will call our search function.

We have also updated our v-for loop to loop over the filteredWorkers.

We are now good to go! We should be able to refresh the page and see a search bar on top of our table. If you search for `ai` this will give 0 results, but if you search for `Ai` you fill get Airi Satou.

Clicking the button is perfectly fine but if we want to enable the search on pressing an enter key, we can do that quite easily:

``` html
<input type="text" v-model="searchString" v-on:keyup.enter="search">
```

With that we are done! In the next chapter we will look at adding sorting so that when we click a heading, our table will re-sort.

## All of the Code

``` html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>PetiteVue Tutorial</title>
        <link rel="icon" href="data:;base64,=">
        <style>
        </style>
    </head>
    <body>
        <div id="app" @vue:mounted="mounted">
            <div v-if="!loaded">Waiting for data...</div>
            <div v-else>
                <input type="text" v-model="searchString" v-on:keyup.enter="search">
                <button @click="search">Search</button>
                <table>
                    <thead>
                        <th>Name</th>
                        <th>Position</th>
                        <th>Office</th>
                        <th>Age</th>
                    </thead>
                    <tbody>
                        <tbody>
                            <tr v-for="worker in filteredWorkers">
                                <td>{{worker.name}}</td>
                                <td>{{worker.position}}</td>
                                <td>{{worker.office}}</td>
                                <td>{{worker.age}}</td>
                            </tr>
                        </tbody>
                    </tbody>
                </table>
            </div>
        </div>
        <script type="module">
            import { createApp } from '/js/petite-vue.es.js'

            async function getWorkers() {
                const workers = [
                    { name: "Airi Satou", position: "Accountant", office: "Tokyo", age: 33},
                    { name: "Angelica Ramos", position: "Chief Executive Officer (CEO)", office: "London", age: 47 },
                    { name: "Cedric Kelly", position: "Senior Javascript Developer", office: "Edinburgh", age: 22 },
                    { name: "Jennifer Chang", position: "Regional Director", office: "Singapore", age: 28 },
                ];
                this.loaded = true;
                return workers;
            }

            function search() {
                this.filteredWorkers = this.searchString === ""
                    ? this.workers
                    : this.workers.filter(wo => Object.values(wo).join("").indexOf(this.searchString) !== -1);
            }

            async function mounted() {
                this.workers = await this.getWorkers();
                this.filteredWorkers = this.workers;
            }

            createApp({
                searchString: "",
                search,
                filteredWorkers: [],

                getWorkers,
                workers: [],
                loaded: false,
                mounted,
            }).mount("#app");
        </script>
    </body>
</html>
```