Project Setup
Enter the following command from your Windows Command Prompt/PowerShell or Linux/OSX Terminal to install Vue CLI globally:
npm install -g @vue/cli
vue --version # Verify installation (v 4.5.11)
If your system does not allow you to installed the CLI globally,
Data Binding
VueJS provides three mechanisms for binding data to your page, only the first two are described below. The third one will be explained later in the context of event handling:
- Use the "mustache" operator
{{xx}}
to bind data as a text node in your HTML - Use
v-bind
to bind data to an attribute of HTML elements
Using The "Mustache" Operator
<template>
<div>
<h2>{{message}}</h2>
<!-- You can use any valid JS expressions -->
<p>{{message.toUpperCase()}}</p>
<span>Length of string:{{message.length + 2}}</span>
</div>
</template>
<script>
export default {
data() {
return {
message: "Hello World"
};
}
};
</script>
Using v-bind
In the following example, the value of the width
attribute of the <img>
element is bound to the variable imgWidth
. Alternatively, v-bind:width
can be shortened to :width
.
<template>
<div>
<img
src="https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
v-bind:width="imgWidth" />
<img
src="https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
:width="imgWidth" />
</div>
</template>
<script>
export default {
data() {
return {
imgWidth: 150
};
}
};
</script>
Using v-bind
with Dynamic Arguments
WARNING
This feature requires VueJS version 2.6.0 or newer
Practical use case, what if you need to include an image but, let's say, in landscape mode you want to bind its width and in portrait mode you want to bind its height. One solution is to use v-if
and v-else
:
<img v-if="iLandscape" src="....." :width="myDimension">
<img v-else src="....." height="myDimension">
Repeat with v-for
Using an array as your data source, each element in the array can be bound using the v-for
directive.
TIP
For performance reasons, VueJS requires elements generated by v-for
be assigned a unique key among the siblings of the same parent. When the array elements are unique, you can use the element itself as a key. Otherwise you can use the element position as the key.
Simple Loop
<template>
<div id="app">
List of atom names
<ul>
<li v-for="a in atoms">{{a}}</li>
</ul>
List of atom names (with item as key):
<ul>
<li v-for="b in atoms" :key="b">{{b}}</li>
</ul>
List of atom names (with position as key):
<ul>
<li v-for="(c,pos) in atoms" :key="pos">{{c}}</li>
</ul>
</div>
</template>
<script>
export default {
data: () => {
return {
atoms: ["Argon", "Carbon", "Neon"]
};
}
};
</script>
Iterate Through Array of Objects
When using v-for
with an array of objects, the key is usually a unique value from the array element or the element position.
<template>
<div id="app">
<ul>
<li v-for="a in atoms" :key="a.symbol">{{a.name}} ({{a.symbol}})</li>
</ul>
</div>
</template>
<script>
export default {
data: () => {
return {
atoms: [
{
name: "Argon",
symbol: "Ar",
weight: 39.8
},
{
name: "Gold",
symbol: "Au",
weight: 196.97
}
]
};
}
};
</script>
Loop with <template>
In some cases, you must loop over a several HTML elements as a group. Consider the following example where each group consists of one <em>
, one <i>
, and text nodes:
<em>Gold</em> (<i>Au</i>)
<em>Silver</em> (<i>Ag</i>)
One solution is to wrap each group in a <span>
or <div>
but this will create a deeper nesting of the elements.
<span v-for="(a,pos) in atoms" :key="pos">
<em>{{a.name}}</em> (<i>{{a.symbol}}</i>)
</span>
If deeper nesting is not preferred, you can wrap each group in a <template>
. Using this approach, the key must be assigned to the "real" elements:
<template v-for="(a,pos) in atoms">
<em :key="`n-${pos}`">{{a.name}}</em> (<i :key="`s-${pos}`">{{a.symbol}}</i>)
</template>
TIP
In the above example, we can't use :key="pos"
because that creates duplicate key (one for <em>
and one for <i>
). To avoid this issue, we use unique strings to distinguish the two: n-
and s-
. So the keys are : n-0
, s-0
, n-1
, s-1
, etc.
<template>
<div id="app">
<template v-for="(a,pos) in atoms">
<b v-bind:key="`name-${pos}`">{{a.name}}</b> (<i
:key="`sym-${pos}`">{{a.symbol}}</i>)
</template>
<table border="1" cellspacing="0" cellpadding="8">
<thead>
<tr>
<th>Symbol</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr v-for="(b,pos) in atoms" v-bind:key="pos">
<td>{{b.symbol}}</td>
<td>{{b.name}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
atoms: [{ name: "Silver", symbol: "Ag" }, { name: "Gold", symbol: "Au" }]
};
}
};
</script>
Conditional
Use v-if
, v-else-if
, and v-else
directives to conditionally include different parts of HTML sections/groups based on specific conditions of your data. For instance, you may have conditional statements below:
if (fuel > 11) {
show ("Plenty of fuel!")
} else if (fuel >= 3 && fuel <= 11) {
show ("Enough to drive....")
} else if() {
show ("....");
} else {
show("Run out of gas!");
}
which translates to VueJS v-if
directives below:
<template>
<div id="app">
<h2>Fuel Gauge</h2>
<p>You have {{fuel}} gallon(s) of fuel</p>
<b v-if="fuel > 11">Plenty of fuel!</b>
<b v-else-if="fuel >= 3 && fuel <= 11">Enough to drive {{fuel * mpg}}
miles</b>
<div v-else-if="fuel > 0">
<h3>WARNING</h3>
<p>Find nearby gas station within {{fuel * mpg}} miles</p>
</div>
<b v-else>Oops! You ran out of gas</b>
</div>
</template>
<script>
export default {
data: () => {
return {
fuel: 2.1,
mpg: 23.5
};
}
};
</script>
Binding
Attribute Binding
<template>
<div id="app">
<ul>
<li v-for="b in birds">
{{b.name}}
<img v-bind:src="b.imageURL" width="300">
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
birds: [
{
name: "Robin",
imageURL:
"https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
},
{
name: "Cardinal",
imageURL:
"https://upload.wikimedia.org/wikipedia/commons/d/da/Cardinal.jpg"
}
]
};
}
};
</script>
Style Binding
Event Handling
<template>
<div id="app">
<p>Move the mouse over each text</p>
<ul>
<li
v-for="b in birds"
v-on:mouseover="updateImage(b.imageURL)"
@mouseleave="resetImage"
>{{b.name}}</li>
</ul>
<img v-bind:src="currentImage" width="200">
</div>
</template>
<script>
export default {
data() {
return {
birds: [
{
name: "Robin",
imageURL:
"https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
},
{
name: "Cardinal",
imageURL:
"https://upload.wikimedia.org/wikipedia/commons/d/da/Cardinal.jpg"
}
],
currentImage: ""
};
},
methods: {
updateImage(url) {
this.currentImage = url;
},
resetImage: function() {
this.currentImage = "";
}
}
};
</script>
<style scoped>
li {
display: inline;
padding: 0 1em;
}
li:hover {
border-bottom: 2px solid rgb(42, 165, 93);
}
</style>
<template>
<div id="app">
<p>Press Shift-Click on the image</p>
<p ref="msg" class="fadeout" v-show="msg.length > 0">{{msg}}</p>
<div @click.shift="showText">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/da/Cardinal.jpg" width="600">
</div>
</div>
</template>
<script>
export default {
data() {
return {
msg: ""
};
},
methods: {
showText() {
this.msg = "Yes";
// Auomatically reset text after 2 seconds
setTimeout(() => {
this.msg = "";
}, 2000);
}
}
};
</script>
<style scoped>
.fadeout {
animation-duration: 2s;
animation-name: fadingout;
opacity: 0;
}
@keyframes fadingout {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
</style>
2-way Binding
<style scoped>
#inputBox {
display: grid;
grid-template-columns: 100px 10em;
grid-template-rows: auto auto;
}
</style>
<template>
<div id="app">
<h2>Atoms</h2>
<p>Your input: {{atoms[selectedAtom]}} Weight: {{atomWeight}}</p>
<select v-model="selectedAtom">
<template v-for="(name,pos) in atoms">
<option :value="pos">{{name}}</option>
</template>
</select>
<div id="inputBox">
<span>Name</span>
<input type="text" v-model="atoms[selectedAtom]">
<span>Weight</span>
<input type="text" v-model.lazy.number="atomWeight">
</div>
</div>
</template>
<script>
export default {
data() {
return {
atoms: [
"Hydrogen",
"Beryllium",
"Boron",
"Carbon",
"Nitrogen",
"Oxygen",
"Fluorine",
"Helium"
],
atomName: "",
atomWeight: 0,
selectedAtom: 0
};
}
};
</script>
Life cycle Functions
<template>
<div id="app">
Invoked callbacks:
<ul>
<li v-for="m in callbacks">{{m}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
callbacks: []
};
},
methods: {},
// beforeCreate() {
// // this.callbacks.push("beforeCreate()");
// },
created() {
this.callbacks.push("created()");
},
beforeMount() {
this.callbacks.push("beforeMount()");
},
mounted() {
this.callbacks.push("mounted()");
},
beforeUpdate() {
// // this.callbacks.push("beforeUpdate()");
console.log("beforeUpdate");
},
updated() {
// this.callbacks.push("updated()");
console.log("updated");
},
beforeDestroy() {
console.log("beforeDestroy()");
},
destroyed() {
console.log("destroyed()");
}
};
</script>