VueJs Directives: Conditional Rendering in Vue

Created on March 31, 2022.


Important

To get the maximum learning value out of this post, make sure you engage in practical programming by running the code blocks provided to see them in action. Click here to learn how to do that.

In the previous post we got introduced to Vue Directives and covered a few of the custom in-built directives such as v-html and v-show.

In this post, we shall cover Vue directives that help to perform conditional rendering within Vue templates.

The v-bind Directive

We’ve learned that the v-bind directive binds the reactive data to the value of HTML tag attributes. Let’s get a bit of depth into it.

Take a look at the following example:

Practice this code!
# run this code on your browser
<html>
  <style>
    img {
      margin: 1em;
    }
  
</style>

  <div id="app">
    <section>
      <h2>When viewer's age is {{ age }}yrs</h2>
      <div v-bind:hidden="age < 13">
        <h3>Allow to see PG and 13+ Movies</h3>
        <img v-bind:src="moviePoster + '1'">
        <img v-bind:src="moviePoster + '2'">
      </div>
      <div v-bind:hidden="age >= 13">
        <h3>Allow to see PG Movie Only</h3>
        <img v-bind:src="moviePoster + '2'">
      </div>
    </section>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          age: 0,
          moviePoster: "https://picsum.photos/150/200?random="
        }
      },
      mounted() {
        const fluctuateAge = () => {
          this.age = (Math.random() * 10 + 5).toFixed();
        }
        let intervalId = setInterval(fluctuateAge, 1500);
      },
    }).mount("#app");
  
</script>
</html>

In the app above, we are veting the viewers permitted to watch movies in Vueland Cinema by comparing the movies’ content rating to the viewers’ ages.

As we can see in this example, just as in the case with the mustache syntax, the variable bound by v-bind within the template is subject to JavaScript operations and is treated as a JavaScript expression. This is demonstrated on the v-bind:src="moviePoster + '1'" where we are doing String concatenation and in v-bind:hidden="age < 13" where we are rendering a logical operation.

Let’s see another example:

Practice this code!
<html>
  <style>
    section {
      display: flex;
      flex-direction: row;
      justify-content: space-around;
    }

    .profit,
    .loss
{
      padding: 2px;
      color: white;
    }

    .loss {
      background-color: red;
    }

    .profit {
      background-color: green;
    }
  
</style>

  <div id="app">
    <h1>Monthly Sales</h1>
    <section>
      <div>
        <h2>Vueland Store</h2>
        <h3>Sales : ${{ storeOneSales.toLocaleString() }}</h3>
        <h3>Profit : ${{ (storeOneSales - monthlyExpenditures).toLocaleString() }}</h3>
        <span class="loss" v-bind:hidden="storeOneSales > monthlyExpenditures">Made Loss</span>
        <span class="profit" v-bind:hidden="storeOneSales < monthlyExpenditures">Made Profit</span>
      </div>
      <div>
        <h2>Vueland Megastore</h2>
        <h3>Sales : ${{ (storeTwoSales).toLocaleString() }}</h3>
        <h3>Profit : ${{ (storeTwoSales - monthlyExpenditures2).toLocaleString() }}</h3>
        <span class="loss" :hidden="storeTwoSales > monthlyExpenditures2">Made Loss</span>
        <span class="profit" :hidden="storeTwoSales < monthlyExpenditures2">Made Profit</span>
      </div>
    </section>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          articlePrice: 5,
          monthlyExpenditures: 1000,
          monthlyExpenditures2: 50000
        }
      },
      computed: {
        storeOneSales() {
          return parseFloat(this.articlePrice * (Math.random() * 50 + 300).toFixed())
        },
        storeTwoSales() {
          return parseFloat(((this.storeOneSales * 3) + (Math.random() * 20000) + 40000).toFixed())
        },
      },
      mounted() {
        const fluctuateArticlePrice = () => {
          this.articlePrice = (Math.random() * 2 + 2).toFixed();
        }
        let intervalId = setInterval(fluctuateArticlePrice, 2000);
      },
    }).mount("#app");
  
</script>
</html>

Inside the section block above, we see that we can also use computed properties within v-bind, and since computed properties give us reactive data, the attribute values are updated when the dependency variables for our computed properties get updated.

We can see that storeOneSales as a computed property depends on the value of articlePrice, also the second computed property - storeTwoSales depends on the value of storeOneSales and hence is updated when its value changes.

The v-bind Directive Shorthand (:)

Vue has a dedicated shorthand for the v-bind directive, the colon :. This shorthand ensures that the overall code-base around this directive is as short as possible since we use the v-bind directive quite often in Vue.

The shorthand syntax involves the elimination of v-bind from v-bind:attribute=“variable” and remaining with the shorthand as the prefix to the attribute resulting in :attribute=“variable”.

You might have noticed that we used this shorthand in place of v-bind in the block regarding the Vueland Megastore in the previous example.

We will be using the shorthand version of v-bind in all the examples here onwards.

Conditional Rendering

If you take a closer look at the two examples above, the template code is a bit verbose. We are using so much logic within the templates, which seems like a future issue in the making or at the least a recipe for maintenance headaches.

We can make the above examples better by performing conditional rendering in them replacing some v-bind directives. We have used one of the conditional directives in the previous post, the v-show directive. Let see how the v-show directive can help us in the first example.

The v-show directive

The v-show directive is a conditional Vue directive that modifies the display style of a HTML element based on the truthiness of the variable passed to it. It adds an in-line display: none style to the HTML element when the variable bound to it resolves to false.

Let’s modify the template, replacing the v-bind directives with v-show logic to better our code.

Practice this code!
<div id="app">
    <section>
      <h2>When viewer's age is {{ age }}yrs</h2>
      <div>
        <h3 v-show="age < 13">Allow to see PG Movie Only</h3>
        <h3 v-show="age >= 13">Allow to see PG and 13+ Movies</h3>
        <img :src="moviePoster + '1'">
        <img v-show="age >= 13" :src="moviePoster + '2'">
      </div>
    </section>
  </div>

As you can see, we’ve abstained from using the hidden attribute and the v-bind directive cutting the template to a single div block instead of two. We only display the text Allow to see PG and 13+ Movies and the 13+ movie poster when conditions apply by using the v-show directive. We show Allow to see PG Movie Only when the inverse is true.

The v-if, v-else-if and v-else Directives

The v-if, v-else-if and v-else directives are other Vue conditional directives that resemble JavaScript’s own if statement logic, but as opposed to JavaScript’s if statement Vue gives us the ability to use that logic within our HTML templates.

Let’s see these directives in action by using them to improve the code inside our second example.

Practice this code!
<div id="app">
    <h1>Monthly Sales</h1>
    <section>
      <div>
        <h2>Vueland Store</h2>
        <h3>Sales : ${{ storeOneSales.toLocaleString() }}</h3>
        <h3>Profit : ${{ (storeOneSales - monthlyExpenditures).toLocaleString() }}</h3>
        <span class="loss" v-if="storeOneSales < monthlyExpenditures">Made Loss</span>
        <span class="profit" v-else>Made Profit</span>
      </div>
      <div>
        <h2>Vueland Megastore</h2>
        <h3>Sales : ${{ (storeTwoSales).toLocaleString() }}</h3>
        <h3>Profit : ${{ (storeTwoSales - monthlyExpenditures2).toLocaleString() }}</h3>
        <span class="loss" v-if="storeTwoSales < monthlyExpenditures2">Made Loss</span>
        <span class="profit" v-else>Made Profit</span>
      </div>
    </section>
  </div>

You can see above that all it took to get the same result was using one statement inside v-if and replacing the opposing statement with the v-else directive.

To use the v-else-if directive, just as in JavaScript, we need more than just two opposing states of a condition. Let’s try adding more scopes of the profit and loss levels in the example above to see how we can use the v-else-if directive.

Practice this code!
<html>
  <style>
    section {
      display: flex;
      flex-direction: row;
      justify-content: space-around;
    }

    .profit,
    .loss
{
      padding: 2px;
      color: white;
    }

    .reasonable {
      color: black;
    }

    .loss {
      background-color: red;
    }

    .loss.reasonable {
      background-color: #ff7777;
    }

    .profit {
      background-color: green;
    }

    .profit.reasonable {
      background-color: #7bff6f;
    }
  
</style>

  <div id="app">
    <h1>Monthly Sales</h1>
    <section>
      <div>
        <h2>Vueland Store</h2>
        <h3>Sales : ${{ storeOneSales.toLocaleString() }}</h3>
        <h3>Profit : ${{ (storeOneSales - monthlyExpenditures).toLocaleString() }}</h3>
        <span class="loss" v-if="!storeOneMadeProfit && storeOneBigMargin">Made a Big Loss</span>
        <span class="loss reasonable" v-else-if="!storeOneMadeProfit && !storeOneBigMargin">Made Reasonable Loss</span>
        <span class="profit" v-else-if="storeOneMadeProfit && storeOneBigMargin">Made a Big Profit</span>
        <span class="profit reasonable" v-else>Made Reasonable Profit</span>
      </div>
      <div>
        <h2>Vueland Megastore</h2>
        <h3>Sales : ${{ (storeTwoSales).toLocaleString() }}</h3>
        <h3>Profit : ${{ (storeTwoSales - monthlyExpenditures2).toLocaleString() }}</h3>
        <span class="loss" v-if="storeTwoSales < monthlyExpenditures2">Made Loss</span>
        <span class="profit" v-else>Made Profit</span>
      </div>
    </section>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          articlePrice: 5,
          monthlyExpenditures: 1000,
          monthlyExpenditures2: 50000
        }
      },
      computed: {
        storeOneSales() {
          return parseFloat(this.articlePrice * (Math.random() * 50 + 300).toFixed())
        },
        storeOneMadeProfit() {
          return this.storeOneSales > this.monthlyExpenditures
        },
        storeOneBigMargin() {
          return this.storeOneMadeProfit
            ? this.storeOneSales - this.monthlyExpenditures > (0.25 * this.monthlyExpenditures)
            : this.monthlyExpenditures - this.storeOneSales > (0.25 * this.monthlyExpenditures);
        },
        storeTwoSales() {
          return parseFloat(((this.storeOneSales * 3) + (Math.random() * 20000) + 40000).toFixed())
        },
        storeTwoLossLevel() { },
        storeTwoProfitLevel() { },
      },
      mounted() {
        const fluctuateArticlePrice = () => {
          this.articlePrice = (Math.random() * 2 + 2).toFixed();
        }
        let intervalId = setInterval(fluctuateArticlePrice, 2000);
      },
    }).mount("#app");
  
</script>
</html>

In the above example, we have modified the Vueland Store monthly sales report to show us the various levels of the profits and losses. We have employed more use of computed properties to help us clean up our code. As evident in this modification, we use the same JavaScript if statement logic, but here inside our template code.

Difference between the v-show and v-if directives

The v-if and v-show directives seem to function somewhat similar, but that is far from the case. Actually, the v-if directive is more brute as it removes the element completely from the DOM while the v-show directive just applies a display: none inline style on the element it’s applied to, meaning, the block still occupies the space on the HTML document but only hidden from vision, thus any DOM manipulations that are to be carried out after have to take its presence into account.

We’ll learn further on the power of the v-if directive when we are covering Vue components.

Next

VueJs Directives: List Rendering

Previous

VueJs Directives: Data Binding in VueJs

Subscribe to the VueNoob Newsletter

[[[ noty.message ]]]

By using this site, you agree that you have read and understand its Privacy Policy.