Conditionally Rendering Elements (v-if, v-else-if, and v-else directives)
Until now, our templates haven’t supported dynamically rendering elements based on boolean conditions. That’s about to change, as we will talk about three directives in this lecture; v-if, v-else-if, and v-else. The names of these directives should pretty self-explanatory, so let’s dive straight into an example.
Suppose that we have a webshop and we want to display how many items are in stock, but that we also want to display a dynamic text based on the item count. This number is stored in a data property named itemsInStock. Right now we have three paragraph elements containing three different texts. We want to display the first one if there are more than 10 items in stock, and if there are between 1 and 10 in stock, we want to display the second text. Otherwise we display the last text.
As for the first condition, I can simply use the v-if directive to check if the itemsInStock property contains a number greater than 10, like this.
<p v-if="itemsInStock > 10">{{ itemsInStock }} in stock.</p>
So in this case, the directive’s expression should evaluate to a boolean value. If the data itemsInStock data property contains a number greater than 10, the paragraph is rendered, and otherwise it is ignored.
For the second paragraph, I will use the v-else-if directive. This comes into play if the v-if directive’s expression evaluates to false. The v-else-if directive also takes an expression, so this time I will check if the itemsInStock data property is greater than zero.
<p v-else-if="itemsInStock > 0">Hurry up, there are just a few items left!</p>
Because this is only evaluated if the v-if directive’s expression evaluates to false, this effectively checks whether or not the itemsInStock property contains a number between 1 and 10.
The last scenario is if none of these two conditions are true, meaning that no items are in stock. This is as easy as adding the v-else directive without any expression.
<p v-else>Too bad, we're all out!</p>
Running the code now, we will see the number of items in stock being printed, because the number is greater than 10. If I change the item count to 8, it seems like there are just a few items in stock, so we better hurry! Changing the number to zero will result in a text stating that we are out of stock. Awesome! Note that since the directives expect an expression, I could also have entered the name of a data property as the expression. In this case, the data property would need to contain a boolean value, i.e. true or false, but the behavior of the directives would remain the same.
Be aware that these three directives actually remove the elements from the DOM in case they evaluate to false, instead of hiding them. This means that if we inspect the paragraph element that is displayed, we are not going to see two other paragraph siblings in the DOM that are hidden by CSS. Instead, these elements are removed and added to the DOM if something changes and the expressions evaluate to true. This applies to any child elements of the element that contains the directive.
Another thing to note is that the v-else-if and v-else directives require that the previous sibling contains the v-if and v-else-if directives respectively. This basically just means that Vue.js requires these three directives to be placed on the same level in the DOM, meaning that the elements must be siblings. So you cannot have an element with the v-if directive and then nest an element with the v-else directive at a different level in the HTML.
But what if you have multiple elements that you want to be affected by a v-if directive, for instance? Say that we have two new paragraph tags that we only want to render if we have lots of items in stock.
<p>Special Offer!</p>
<p>Looks like we have tons of products in stock... Save 20% if you order now!</p>
How can we only display these two elements if we have more than 50 items in stock? We could wrap multiple elements within a div element, but this would also change our markup, which may be undesireable and cause you to have to use CSS to fix the appearance. Instead, you can use the template element, which was added in HTML5. Using this element means that the template wrapper element is not added to the DOM, but the inner contents are. I will add a template element that wraps the paragraphs and use the v-if directive to check if there are more than 50 items in stock.
<template v-if="itemsInStock > 50">
<p>Special Offer!</p>
<p>Looks like we have tons of products in stock... Save 20% if you order now!</p>
</template>
Now I will just change the items in stock to be 51 so we can see it in action.
new Vue({
el: '#app',
data: {
itemsInStock: 51
}
});
Now the paragraphs appear, but if we inspect the DOM, we can see that there is no sign of the template element. This is great, because in this case, we don’t need to use a div element as a wrapper, which then affects the DOM and potentially the appearance of the page.
Here is what you will learn:
- How to build advanced Vue.js applications (including SPA)
- How Vue.js works under the hood
- Communicating with services through HTTP
- Managing state of large applications with Vuex
- ... and much more!