bekwam courses

Vue.js Templates and Loops

February 7, 2019

This article demonstrates the Vue.js <template> tag and v-for directive. Like a standard <div>, the template tag wraps content into a single node and can be used in a v-for loop. One difference is that since template doesn't render, the child contents can be conditionally displayed. This example contains three template / v-for /v-if blocks that output in each of the three divs unconditionally and a second div unless it's the last iteration.

The following video shows the demo app. Digit ratio buttons are manipulated to produce binary numbers in various bases. The app forms a binary number based on radio button selections that toggle the digits. The summation of the digits is presented in a text box. The user has the option of rendering the number using several bases: binary, base 2; octal, base 8; decimal, base 10; hex, base 16.

To run the demo yourself, visit Bekwam.org Binary Project.

This screenshot shows the structure of the web page. The Exponent Headers Row is a sequence of <div> elements containing a binary exponent separated by plus signs. Although this could be coded as 16 <div> tags, you'll see later that this is a loop. The radio buttons, labeled as Digit Radio Buttons, are arranged in a pair of rows and like the exponents header are implemented using a loop. The Value Text Box binds to a computed property. Select Base Hyperlinks changes the input control in Value Text Box when different links are selected.

Browser Screenshot
App Marked Up Into Sections

Javascript

The Javascript supporting the app is in a file "main.js". A single Vue instance contains a pair of properties: binaryDigits and showing. binaryDigits is an 8-element array of 1's and 0's that are bound to the Digit Radio Buttons. "showing" is a variable bound to selected link in Select Base Hyperlinks. There are several computed properties. decimalValue sums the elements in binaryDigits based on their associated exponent value. binaryValue, octalValue, and hexValue use toString(base) to render decimalValue in different bases. selectView records the selected link.

    
const app = new Vue({
    el: '#app',
    data: {
        binaryDigits: [0, 0, 0, 0, 0, 0, 0, 0],
        showing: 'Binary'
    },
    computed: {
        decimalValue() {
            var sum = 0
            for( var n = 0; n < this.binaryDigits.length; n++ ) {
                sum += this.binaryDigits[n] * Math.pow(2, n)
            }
            return sum;
        },
        octalValue() {
            return this.decimalValue.toString(8)
        },
        hexValue() {
            return this.decimalValue.toString(16).toUpperCase()
        },
        binaryValue() {
            return this.decimalValue.toString(2)
        }   
    },
    methods: {
        selectView(view) {
            this.showing = view
        }
    }
})
    
    

HTML

Most of the code is in index.html. There is a toplevel <div> container which is the app el instance "app". The entire index.html can be studied using View Source from the running demo or from the zip file attached below. The next sections will go over the interesting parts of index.html and leave out HTML5 elements like <head> which aren't an area of focus.

Exponent Headers Row

Starting with the Exponent Headers Row, a spacer <div> is output. Next, a <template> is set up with a range v-for loop. The template tag will not render. For each iteration, a <div> is output which is an exponent with a value based on the loop position. Vue.js doesn't have a negative step ("countdown") for loop like some other languages, so I'm using a math expression that subtracts the index from the number of elements. A second <div> with a plus sign is conditionally output; it won't be output if we're on the last item.


<!-- exponent headers -->
<div class="box"></div>
<template v-for="n in 8">
	<div class="box">2<sup>{{ 8-n }}</sup></div>
	<div class="box" v-if="n < 8">+</div>
</template>

Digit Radio Buttons

The Digit Radio Buttons rows have a similar looping structure to Exponent Headers Row. One big difference sis that the radio buttons are dynamic and contain references to Vue.js variables. Each radio button pair is given a distinct name based on its position in the loop. For example, the first 0 and 1 pair is given the name "binaryDigit0". The name is shared by two radio buttons so that selecting one toggles the other.

The v-model expression binds the radio button to an element in Vue's binaryDigits array. The index is based on the loop position substracted from the number of elements.

As with the Exponent Headers Row, the last div is skipped. I'm using CSS Grid in this example and need to make sure that each of the three rows contains the same number of columns so that everything lines up.


<!-- 0 radio buttons -->
<div class="box">0</div>
<template v-for="n in 8">
	<div class="box">
		<input type="radio" :name="'binaryDigit' + (8-n)" v-model="binaryDigits[8-n]" value="0">
	</div>
	<div class="box" v-if="n < 8"></div>
</template>

<!-- 1 radio buttons -->
<div class="box">1</div>
<template v-for="n in 8">
		<div class="box">
			<input type="radio" :name="'binaryDigit' + (8-n)" v-model="binaryDigits[8-n]" value="1">
		</div>
		<div class="box" v-if="n < 8"></div>
</template>

Value Text Box

The Value Text Box is actually four input text controls. Only one of these controls will be shown, based on the showing variable. This sleight-of-hand makes the binding simpler since there is a one-to-one correspondence between the computed properties and the text controls.


<div>
	=
	<input type="text" readonly v-model="binaryValue" v-show="showing == 'Binary'">
	<input type="text" readonly v-model="octalValue" v-show="showing == 'Octal'">
	<input type="text" readonly v-model="decimalValue" v-show="showing == 'Decimal'">
	<input type="text" readonly v-model="hexValue" v-show="showing == 'Hex'">
</div>

Select Base Hyperlinks

The last section of interest is Select Base Hyperlinks. This is a set of four hyperlinks. Clicking on a hyperlink using v-on:click will invoke the selectView() method with one of four values: Binary, Octal, Decimal, Hex. This will set the showing variable. showing hides the unused text boxes and sets an active-link CSS style, marking the current selection for the user.


<div class="links-container">
	<a v-on:click="selectView('Binary')" :class="{ 'active-link': showing == 'Binary' }">Binary</a> |
	<a v-on:click="selectView('Octal')" :class="{ 'active-link': showing == 'Octal' }">Octal</a> |
	<a v-on:click="selectView('Decimal')" :class="{ 'active-link': showing == 'Decimal' }">Decimal</a> |
	<a v-on:click="selectView('Hex')" :class="{ 'active-link': showing == 'Hex' }">Hex</a> 
</div>

CSS

The layout is using CSS Grid. To review the CSS used in this app, look for styles.css in the running demo or in the zip file linked below.

To see the complete code, run the app at Bekwam.org or download the source in a zip file here.

This article presented some code featuring template, v-for, and v-if. This triple was used to render array without resorting to "brute force" which would be the copy-paste-tweaking of repeated elements. Although the loops might look unfamiliar at first in an HTML page, the payoff is later when you have a reduced amount of duplicated code around the app. That means you can edit a single line and be sure that it affects the whole set. Moreover, if configuration management is used (ex "Git"), it's easier for a third-party to track changes to a single line instead of multiple changes across similar lines.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc