v0.3 - Inputs and Computed variables
Input binding
Rendering lists is fun and all, but we want our users to interact with our application! So lets cover binding to inputs. Vue has an attribute called v-model we can use to capture input from standard HTML input elements.
Here we are adding the container class (part of milligram.css) so we get some spacing from the side of the window.
We are also adding an input field with the v-model attribute, it has been given the value of search this binds the value in the field to search in our Vue instance. We have also included search in the markup right after the input so we can make sure everything is hooked up correctly. There is also a placeholder and a width applied for appearances.
index.html
<div id="app" class="container">
<h1>
{{message}}
</h1>
<input v-model="search"
type="text"
style="width:200px"
placeholder="Search..">
{{search}}
<div v-for="f in food" v-if="f.name[0] == 'B'">
{{f.icon}} {{f.name}}
</div>
</div>
All we need to do in our data is add search with and empty string value.
app.js
const app = new Vue({
el: '#app',
data: {
search: '',
message: 'Good morning, have a 🥐',
food: [..]
}
})
If we type something into the input field, it will show up as we type.

This is great, but what if we want to use that value for something. We can use it in the template. indexOf returns the index of what is provided or -1 if it isn't found. Here we are seeing if the value of search is found in the name of the food. If it is found, it will show.
index.html
<div id="app" class="container">
<h1>
{{message}}
</h1>
<input v-model="search"
type="text"
style="width:200px"
placeholder="Search..">
{{search}}
<div v-for="f in food"
v-if="f.name.indexOf(search) !== -1">
{{f.icon}} {{f.name}}
</div>
</div>
This is ok for simple filters like ours. But what if your search became much more complicated? Putting all that logic in the template is messy, we cant flex our JS muscle.
This is where computed variables come in.
Computed variables
Computed variables are great for cases like this, where we want to take a piece of data, and transform it whenever its dependencies (in this case, search) change.
To write one we create a new key in our app called (you guessed it) computed. We put the function that defines our computed variable within there. Unlike in the HTML, variables in the Vue instance require that we reference them using this. Here we are using the Array prototype filter to apply the exact same filter as before.
app.js
const app = new Vue({
el: '#app',
computed: {
filtered_food() {
return this.food
.filter(f => f.name.indexOf(this.search) !== -1)
}
},
data: {
search: '',
message: 'Good morning, have a 🥐',
food: [...]
}
})
We can simplify our v-for to just reference the computed variable. This is better for performance, and keeps our code clean.
index.html
<div id="app" class="container">
<h1>
{{message}}
</h1>
<input v-model="search"
type="text"
style="width:200px"
placeholder="Search..">
{{search}}
<div v-for="f in filtered_food">
{{f.icon}} {{f.name}}
</div>
</div>
If you refresh your page and try some searches you will see that we have the exact same functionality as before, thrilling.
Alright some lets make it a bit more complicated then, how about we add sorting!
We are going to use the Array prototype sort to order these foods alphabetically. In our case we will need to supply a compare function to sort so that it knows which items out-rank others. Here we are simply comparing the names.
app.js
filtered_food() {
return this.food
.filter(f => f.name.indexOf(this.search) !== -1)
.sort((a, b) => {
if (a.name > b.name) return 1;
if (a.name < b.name) return -1;
return 0;
})
}
Refresh and take a look at the list, it is now sorted from 🥓 to 🌮.
