Master Vue - Js in 6 Days
Master Vue - Js in 6 Days
js
in 6 Days
Become a Vue.js Expert in
Under a Week
—
Eric Sarrion
Master Vue.js
in 6 Days
Become a Vue.js Expert
in Under a Week
Eric Sarrion
Master Vue.js in 6 Days: Become a Vue.js Expert in Under a Week
Eric Sarrion
VIRY CHATILLON, France
Introduction����������������������������������������������������������������������������������������xv
iii
Table of Contents
iv
Table of Contents
v
Table of Contents
vi
Table of Contents
vii
Table of Contents
viii
Table of Contents
ix
Table of Contents
Index�������������������������������������������������������������������������������������������������347
x
About the Author
Eric Sarrion is a trainer, a web developer, and an independent consultant.
He has been involved in all kinds of IT projects for over 30 years. He is also
a long-time author of web development technologies and is renowned for
the clarity of his explanations and examples. He resides in Paris, France.
Some of his recent publications include Master React in 5 Days (Apress),
ChatGPT for Beginners (Apress), and JavaScript from Frontend to Backend
(Packt).
xi
About the Technical Reviewer
Fabio Claudio Ferracchiati is a senior consultant and analyst/developer
using Microsoft technologies. He works for Bluarancio (www.bluarancio.
com). He is a Microsoft Certified Solution Developer as well as a
Microsoft Certified Application Developer for .NET, a Microsoft Certified
Professional, and a prolific author and technical reviewer. Over the past
ten years, he’s written articles for Italian and international magazines and
coauthored more than ten books on a variety of computer topics.
xiii
Introduction
Welcome to the exciting world of Vue.js! You are holding in your hands a
comprehensive guide to mastering this JavaScript framework in just six
days. Whether you are a novice developer seeking a solid starting point or
a seasoned professional looking to deepen your skills, this book is tailored
for you.
Vue.js has emerged as one of the most popular and revered JavaScript
frameworks in the web development industry. Its simplicity, flexibility,
and outstanding performance have made it a preferred choice for creating
modern web applications, be it small interactive applications or large-scale
projects.
This book is structured into six days of learning, each focusing on key
aspects of Vue.js. You will embark on a progressive journey, from creating
components to handling events, delving into HTTP requests, custom
directives, and composables. Each day will bring you new skills and
practical knowledge that you can immediately apply to your projects.
What will you find within these pages? Clear explanations, concrete
examples, hands-on exercises, and development tips. We will guide you
through the fundamental concepts of Vue.js and assist you in becoming a
proficient Vue.js developer.
Prepare for an exciting journey. Whether it’s for creating professional
web applications or simply expanding your horizons in web development,
this book will equip you with the necessary skills to excel in the world
of Vue.js. So dive into this adventure and become a Vue.js master in just
six days!
xv
CHAPTER 1
Day 1: Mastering
Components in Vue.js
Welcome to Chapter 1 of our journey to master Vue.js in just six days.
During this first day, we will delve into the world of Vue.js, starting by
understanding why it is so powerful and exploring its key concept, the
Virtual DOM. We will then follow a step-by-step process to create our first
Vue.js application, guiding you through the installation of Node.js and Vue
CLI, as well as examining the default files generated in a Vue.js application.
We will also explore the structure of a Vue.js application, examining
configuration files, directories, and essential components. Additionally,
we will demonstrate how to break down an application into Vue.js
components, adhering to naming conventions and creating our first
component.
Finally, we will delve into fundamental aspects such as reactivity
usage, defining methods and computed properties in a Vue.js component,
and managing the lifecycle.
2
Chapter 1 Day 1: Mastering Components in Vue.js
3
Chapter 1 Day 1: Mastering Components in Vue.js
Virtual DOM
The Virtual DOM (Document Object Model) is a key concept in many
modern JavaScript frameworks, including Vue.js. It is a lightweight and
efficient abstraction of the real DOM, which represents the structure of
a web page in the browser’s memory. The goal of the Virtual DOM is to
improve performance and the efficiency of DOM updates by minimizing
direct manipulations, which can be time-consuming.
Let’s now explore how the binding between the browser’s DOM and
Vue.js’s Virtual DOM works.
4
Chapter 1 Day 1: Mastering Components in Vue.js
5
Chapter 1 Day 1: Mastering Components in Vue.js
File MyCounter.vue
<script setup>
import { ref } from "vue";
</script>
<template>
<div>
<p>Counter: {{ count }}</p>
<button @click="increment()">Increment</button>
</div>
</template>
6
Chapter 1 Day 1: Mastering Components in Vue.js
7
Chapter 1 Day 1: Mastering Components in Vue.js
8
Chapter 1 Day 1: Mastering Components in Vue.js
Once Vue CLI is installed, you can use the vue create command to
create the Vue.js application.
9
Chapter 1 Day 1: Mastering Components in Vue.js
The system prompts for the desired version of Vue.js. We retain the
default selection of the current version 3 by pressing the Enter key on the
keyboard.
The Vue.js application named vueapp begins to be created:
10
Chapter 1 Day 1: Mastering Components in Vue.js
11
Chapter 1 Day 1: Mastering Components in Vue.js
Once the Vue.js application is created, the next step is to start it.
12
Chapter 1 Day 1: Mastering Components in Vue.js
The npm run serve command starts a Node.js server on which the
Vue.js application will run.
Once the Node.js server is launched, the terminal screen becomes the
following:
13
Chapter 1 Day 1: Mastering Components in Vue.js
Figure 1-8. Completion of the server launch process with the Vue.js
application
14
Chapter 1 Day 1: Mastering Components in Vue.js
15
Chapter 1 Day 1: Mastering Components in Vue.js
File package.json
{
"name": "vueapp",
"version": "0.1.0",
"private": true,
16
Chapter 1 Day 1: Mastering Components in Vue.js
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^3.2.13"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
17
Chapter 1 Day 1: Mastering Components in Vue.js
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}
In this file, you’ll find the list of dependencies that our application
needs to run, along with their respective versions.
The “scripts” key allows the execution of commands such as npm run
serve, which launches the development server on port 8080.
Additional scripts can be added. For example, let’s insert a new script
“start” in the “scripts” section that starts the Vue.js application on port
3000 instead of the default port 8080.
"scripts": {
"serve": "vue-cli-service serve",
"start": "vue-cli-service serve --port 3000",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
The command npm run start starts the server on port 3000.
18
Chapter 1 Day 1: Mastering Components in Vue.js
19
Chapter 1 Day 1: Mastering Components in Vue.js
20
Chapter 1 Day 1: Mastering Components in Vue.js
Let’s see the content of the index.html file. This content is rarely
modified as it incorporates a crucial <div> HTML element for the
functioning of the Vue.js application.
File public/index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-
scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.
title %> doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
21
Chapter 1 Day 1: Mastering Components in Vue.js
Now let’s examine the content of the src directory, which will help us
understand the purpose of the previous <div> element.
Let’s examine in detail each file and directory listed in the src
directory. We’ll start with the main.js file.
Step 5: src/main.js
The main.js file is crucial for starting a Vue.js application. Let’s see its
content:
File src/main.js
createApp(App).mount('#app')
22
Chapter 1 Day 1: Mastering Components in Vue.js
In summary, the code in the main.js file creates an instance of the Vue.
js application using the App component as the root component and then
mounts this instance on an element with the id "app" in the DOM. This
effectively initializes the Vue.js application and makes it ready to be
displayed and used in the browser.
23
Chapter 1 Day 1: Mastering Components in Vue.js
Step 6: src/App.vue
We explained previously that the App.vue file is associated with the App
component, which will be displayed in the HTML page. The App component
describes the structure of our Vue.js application using the syntax provided
by the Vue.js framework. Here is the content of the App.vue file:
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
24
Chapter 1 Day 1: Mastering Components in Vue.js
1. <template> section
2. <script> section
3. <style> section
25
Chapter 1 Day 1: Mastering Components in Vue.js
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize
this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank"
rel="noopener">vue-cli documentation</a>.
26
Chapter 1 Day 1: Mastering Components in Vue.js
</p>
<h3>Installed CLI Plugins</h3>
<ul>
li><a href="https://github.com/vuejs/vue-cli/tree/
dev/packages/%40vue/cli-plugin-babel" target="_blank"
rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/
dev/packages/%40vue/cli-plugin-eslint" target="_blank"
rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank"
rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank"
rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank"
rel="noopener">CommunityChat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank"
rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank"
rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank"
rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank"
rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-
devtools" target="_blank" rel="noopener">
vue-devtools</a></li>
27
Chapter 1 Day 1: Mastering Components in Vue.js
<li><a href="https://vue-loader.vuejs.org"
target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue"
target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
28
Chapter 1 Day 1: Mastering Components in Vue.js
a {
color: #42b983;
}
</style>
30
Chapter 1 Day 1: Mastering Components in Vue.js
31
Chapter 1 Day 1: Mastering Components in Vue.js
32
Chapter 1 Day 1: Mastering Components in Vue.js
The only exception to this rule is the App component, which is the only
one written with a single word.
Once the MyCounter component is created, it can be used in the
<template> sections as <MyCounter> or <my-counter>. Both forms of
writing are equivalent in Vue.js, although the <MyCounter> syntax is
recommended.
File src/components/MyCounter.vue
<script>
</script>
<template>
</template>
<style scoped>
</style>
33
Chapter 1 Day 1: Mastering Components in Vue.js
The <script> and <style> sections are not necessary but are present
as they may contain added instructions later. The MyCounter component
needs to be inserted into the App component to be displayed within it.
Indeed, the App component currently uses the HelloWorld component
seen previously. Let’s modify the App component to incorporate the
MyCounter component.
The App component can be written in two ways, depending on the Vue.
js syntax one wishes to use:
Using the Options API syntax, the App.vue file becomes the following:
<script>
import MyCounter from './components/MyCounter.vue'
export default {
components : {
MyCounter : MyCounter
// As the key and the value are identical, we can also
write more simply:
// MyCounter
}
}
</script>
<template>
<MyCounter />
</template>
<style scoped>
</style>
34
Chapter 1 Day 1: Mastering Components in Vue.js
The MyCounter.vue file is imported into the App component using the
import statement in the <script> section of the component. Then, the
MyCounter component is displayed in the <template> section using the
<MyCounter /> tag.
In the <script> section, we added the export default statement of
an object with the components option describing the components used
in the App component. This is the traditional way of using the syntax with
options, proposed in version 2 of Vue.js. It is called the Options API syntax.
Another syntax is possible, introduced from version 3 of Vue.js. It is
called the Composition API syntax.
Let’s use the Composition API syntax to write the App component in
the App.vue file:
<script setup>
import MyCounter from './components/MyCounter.vue'
</script>
<template>
<MyCounter />
</template>
<style scoped>
</style>
Here, note the setup attribute added to the <script> element. This
attribute in the <script> tag allows importing the MyCounter.vue file
without having to explicitly declare the MyCounter component within the
App component. This is a convenience added in Vue.js version 3, known as
the Composition API syntax.
Let’s verify that both versions of the App component work by displaying
the URL http://localhost:8080.
35
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
</script>
<template>
<h1> MyCounter Component </h1>
</template>
<style scoped>
h1 {
font-family:papyrus;
36
Chapter 1 Day 1: Mastering Components in Vue.js
font-size:20px;
}
</style>
Let’s now explain the use of the scoped attribute in the <style> tag.
<template>
<div>
<p class="red-text">MyCounter Component</p>
</div>
37
Chapter 1 Day 1: Mastering Components in Vue.js
</template>
<style>
.red-text {
color: red;
}
</style>
In this example, the CSS class .red-text is defined in the global style
of the component. It could potentially impact other components if the
class is used elsewhere in the application.
<template>
<div>
<p class="red-text">MyCounter Component</p>
</div>
</template>
<style scoped>
.red-text {
color: red;
}
</style>
In this example, the .red-text class is defined within a <style scoped> tag,
indicating that it will only apply to elements within the current component.
The styles defined here will have no impact on other components.
The scoped attribute is particularly useful for avoiding style conflicts
between components and maintaining style isolation. It facilitates the
creation of reusable components and simplifies style management in a
Vue.js application. Each component can define its styles without concern
for styles defined in other components, contributing to better code
organization and maintainability.
38
Chapter 1 Day 1: Mastering Components in Vue.js
39
Chapter 1 Day 1: Mastering Components in Vue.js
Click the “Add to Firefox” button. Next, display the console by pressing
the F12 key on the keyboard. Then, select the Vue menu by clicking >>.
40
Chapter 1 Day 1: Mastering Components in Vue.js
41
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
import MyCounter from './components/MyCounter.vue'
</script>
<template>
<MyCounter />
</template>
<script setup>
42
Chapter 1 Day 1: Mastering Components in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
The Composition API is used here within the <script setup> syntax.
The ref() method, defined in Vue.js, is usable because it is imported with
the statement import { ref } from "vue". All Vue.js methods used in
our programs must be imported in this manner before they can be utilized.
A slight variation of this syntax is explained in the following section.
The reactive variable count is created and initialized with the
statement count = ref(value). Subsequently, the count variable can be
used in the <template> section, as mentioned earlier. Let’s display the URL
http://localhost:8080 in the browser:
43
Chapter 1 Day 1: Mastering Components in Vue.js
<button @click="count++">count+1</button>
44
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
45
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
function increment() {
count.value++; // One accesses the value of count using
count.value
}
</script>
46
Chapter 1 Day 1: Mastering Components in Vue.js
<template>
<h3>MyCounter Component</h3>
</template>
47
Chapter 1 Day 1: Mastering Components in Vue.js
Note that the increment() function can also be defined using the ES6
syntax. Instead of:
function increment() {
count.value++; // One accesses the value of count using
count.value
}
48
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
import { ref, computed } from "vue";
</script>
<template>
49
Chapter 1 Day 1: Mastering Components in Vue.js
<h3>MyCounter Component</h3>
Reactive variable count: <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br /><br />
<button @click="increment()">count+1</button>
</template>
50
Chapter 1 Day 1: Mastering Components in Vue.js
51
Chapter 1 Day 1: Mastering Components in Vue.js
Let’s write these methods in the MyCounter component and use them
to display traces in the console.
<script setup>
import { ref, computed, onBeforeMount, onMounted,
onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted }
from "vue";
52
Chapter 1 Day 1: Mastering Components in Vue.js
// Lifecycle Methods
onBeforeMount(() => {
console.log("onBeforeMount: The component is about to be
mounted in the DOM.");
});
onMounted(() => {
console.log("onMounted: The component has been mounted in
the DOM.");
});
onBeforeUpdate(() => {
console.log("onBeforeUpdate: The component is about to be
updated.");
});
onUpdated(() => {
console.log("onUpdated: The component has been updated.");
});
onBeforeUnmount(() => {
console.log("onBeforeUnmount: The component is about to be
unmounted.");
});
onUnmounted(() => {
console.log("onUnmounted: The component has been
unmounted.");
});
53
Chapter 1 Day 1: Mastering Components in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count: <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br /><br />
<button @click="increment()">count+1</button>
</template>
Let’s run the previous program and observe the traces displayed in the
console:
54
Chapter 1 Day 1: Mastering Components in Vue.js
We can see that the component is first created in memory and then
mounted in the DOM.
Let’s click the “count+1” button. This will update the component by
incrementing the reactive variable count:
55
Chapter 1 Day 1: Mastering Components in Vue.js
56
Chapter 1 Day 1: Mastering Components in Vue.js
57
Chapter 1 Day 1: Mastering Components in Vue.js
Let’s see how to use these lifecycle methods so that the MyCounter
component displays a counter that increments every second (instead
of using the button click as before). To achieve this, we need to use a
timer with the setInterval() method of JavaScript. The timer will start
in one of the methods indicating the creation of the component (e.g.,
the onMounted() method), and it will be stopped in one of the methods
indicating the removal of the component (e.g., the onUnmounted()
method).
We define two new methods in the MyCounter component, which are
the start() and stop() methods:
<script setup>
import { ref, computed, onMounted, onUnmounted } from "vue";
let timer;
58
Chapter 1 Day 1: Mastering Components in Vue.js
}, 1000);
};
onMounted(() => {
start();
});
onUnmounted(() => {
stop();
});
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count: <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
</template>
59
Chapter 1 Day 1: Mastering Components in Vue.js
61
Chapter 1 Day 1: Mastering Components in Vue.js
If the answer to these two questions is to keep the current value, just
use the ref() method to create the reactive variable. The customRef()
method would not add anything more in this case.
The customRef(callback) method uses the callback(track,
trigger) function, which allows returning an object with the properties {
get, set }, which are functions defining the reading of the variable (with
the get() function) and the modification of the variable (with the set()
function).
62
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
import { customRef } from 'vue';
function increment() {
console.log("before increment value");
count.value += 1; // Increment the value of the variable.
console.log("after increment value");
}
63
Chapter 1 Day 1: Mastering Components in Vue.js
</script>
<template>
</template>
64
Chapter 1 Day 1: Mastering Components in Vue.js
65
Chapter 1 Day 1: Mastering Components in Vue.js
We can see that at the program’s launch, the get() method is called to
retrieve the variable’s value and display it in the component.
Next, let’s click the “count+1” button.
66
Chapter 1 Day 1: Mastering Components in Vue.js
67
Chapter 1 Day 1: Mastering Components in Vue.js
then update the value (via set()). Afterward, a final read (via get())
is performed, which corresponds to displaying the new value in the
component.
Now that we have seen the internal functioning of customRef(), let’s
use it to produce more interesting results.
<script setup>
import { customRef } from 'vue';
68
Chapter 1 Day 1: Mastering Components in Vue.js
return value;
},
set(newValue) {
// Update the value and trigger reactivity.
if (newValue <= maximum) value = newValue;
trigger();
}
};
});
function increment() {
count.value += 1; // Increment the value of the variable.
}
function decrement() {
count.value -= 1; // Decrement the value of the variable.
}
</script>
<template>
</template>
69
Chapter 1 Day 1: Mastering Components in Vue.js
We use a variable named maximum with a value of 10. Since this variable
will not be modified, there is no need to make it reactive. The count
variable is managed by customRef(). The value of the variable managed by
customRef() is modified only if it is less than the maximum value. When
the maximum value (here, 10) is reached, the counter is locked:
70
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
import { customRef } from 'vue';
71
Chapter 1 Day 1: Mastering Components in Vue.js
function increment() {
count.value += 1; // Increment the value of the variable.
}
function decrement() {
count.value -= 1; // Decrement the value of the variable.
}
</script>
<template>
</template>
72
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
73
Chapter 1 Day 1: Mastering Components in Vue.js
function increment() {
count.value += 1; // Increment the value of the variable.
}
function decrement() {
count.value -= 1; // Decrement the value of the variable.
}
</script>
<template>
</template>
74
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
import { customRef } from "vue";
75
Chapter 1 Day 1: Mastering Components in Vue.js
</script>
<template>
76
Chapter 1 Day 1: Mastering Components in Vue.js
<hr>
Current date : {{date}}
<hr>
Date (MM-DD-YYYY) : <b>{{ dateMMDDYYYY }}</b>
<br><br>
Date (DD-MM-YYYY) : <b>{{ dateDDMMYYYY }}</b>
<br><br>
Date (MM/DD/YYYY) : <b>{{ dateMMDDYYYY_slash }}</b>
<br><br>
Date (DD/M/YYYY) : <b>{{ dateDDMMYYYY_slash }}</b>
<br><br>
</template>
77
Chapter 1 Day 1: Mastering Components in Vue.js
78
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
</script>
<template>
<hr>
Current date : {{date}}
79
Chapter 1 Day 1: Mastering Components in Vue.js
<hr>
Date (MM-DD-YYYY) : <b>{{ dateMMDDYYYY }}</b>
<br><br>
Date (DD-MM-YYYY) : <b>{{ dateDDMMYYYY }}</b>
<br><br>
Date (MM/DD/YYYY) : <b>{{ dateMMDDYYYY_slash }}</b>
<br><br>
Date (DD/M/YYYY) : <b>{{ dateDDMMYYYY_slash }}</b>
<br><br>
</template>
<script setup>
import { customRef } from 'vue';
80
Chapter 1 Day 1: Mastering Components in Vue.js
track();
return value.toUpperCase();
},
set(newValue) {
// update the value and trigger reactivity
value = newValue;
trigger();
}
};
});
</script>
<template>
<b>Uppercase Input:</b>
<br><br>
Variable name: <input type="text" v-model="name" />
<br><br>
Variable name: {{ name }}
</template>
81
Chapter 1 Day 1: Mastering Components in Vue.js
82
Chapter 1 Day 1: Mastering Components in Vue.js
<script setup>
</script>
<template>
<b>Uppercase Input:</b>
<br><br>
Variable name: <input type="text" v-model="name" />
<br><br>
Variable name: {{ name }}
</template>
83
Chapter 1 Day 1: Mastering Components in Vue.js
The component is initialized with the variable name set to the value
“eric”, which will be immediately displayed in uppercase in both the input
field and the following text.
Conclusion
We have come a long way during this first day dedicated to mastering
Vue.js! We have covered the necessary steps for creating the first Vue.js
application, from installing Vue.js to analyzing the generated files.
We have also delved into key concepts such as component structuring,
reactivity, defining methods and computed properties, as well as
managing the lifecycle of components.
In the upcoming chapters, we will further deepen our Vue.js skills
and explore more advanced concepts to create high-performance web
applications. So get ready for the exciting continuation of this learning
journey!
84
CHAPTER 2
Day 2: Mastering
Directives in Vue.js
In this chapter, we will delve into directives in Vue.js, a key element
for mastering this front-end library. Directives allow us to add new
functionalities to HTML elements on the page.
We will explore several essential directives, such as v-bind, v-if,
v-else, v-show, and v-for, providing detailed steps to understand how
they function.
Next, we will focus on the v-model directive, which is particularly
useful for two-way data binding in forms.
Finally, we will address the use of modifiers in Vue.js, which allow us to
modify the behavior of directives.
Counter from 10 to 20
Counter from 10 to 20
In this case, the numeric values 10 and 20 are passed to the MyCounter
component. To indicate a counter that starts at 10 but never stops, the end
attribute is omitted in the MyCounter component’s definition:
86
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
</template>
<style scoped>
</style>
87
Chapter 2 Day 2: Mastering Directives in Vue.js
Indeed, specifying :init instead of just init for the attribute name
indicates that the following value is a JavaScript expression, specifically the
numeric value 10 and not the string “10”.
We have seen how to write and use the MyCounter component in
various forms with the init and end attributes. Let’s now explore how the
MyCounter component is written to make use of these attributes.
88
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
import { ref, computed, onMounted, onUnmounted, defineProps }
from 'vue';
let timer;
onMounted(() => {
start();
});
89
Chapter 2 Day 2: Mastering Directives in Vue.js
onUnmounted(() => {
stop();
});
</script>
<template>
<h3>MyCounter Component</h3>
init : {{init}} => end : {{end}}
<br /><br />
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
</template>
<script setup>
</script>
90
Chapter 2 Day 2: Mastering Directives in Vue.js
<template>
</template>
The counter starts at the value 10 and ends at the value 20. Let’s run
the program:
The counter starts from the value 10 and will stop when it reaches the
value 20.
91
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, defineProps }
from 'vue';
let timer;
92
Chapter 2 Day 2: Mastering Directives in Vue.js
onMounted(() => {
start();
});
onUnmounted(() => {
stop();
});
</script>
<template>
<h3>MyCounter Component</h3>
init : {{init}} => end : {{end}}
<br /><br />
93
Chapter 2 Day 2: Mastering Directives in Vue.js
94
Chapter 2 Day 2: Mastering Directives in Vue.js
95
Chapter 2 Day 2: Mastering Directives in Vue.js
v-bind Directive
The v-bind directive allows using attribute values that will be reactive,
similar to reactive variables used in HTML.
For example, let’s use the previous counter, where the value
increments upon clicking the “count+1” button. Suppose we want to
display the counter value in an input field. For this, we would like to write
something like <input type="text" value="{{count}}" />. Indeed, it is
hoped that, thanks to the reactivity of the count variable, the value of the
input field will be updated when the counter is incremented.
Let’s write this in the template of the MyCounter component. The
MyCounter component becomes as follows:
Display the count variable in the value attribute of an input field (file
src/components/MyCounter.vue)
<script setup>
</script>
96
Chapter 2 Day 2: Mastering Directives in Vue.js
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br/>
Input : <input type="text" value="{{count}}" />
<br/><br/>
<button @click="increment()">count+1</button>
</template>
<script setup>
</script>
<template>
<MyCounter />
</template>
97
Chapter 2 Day 2: Mastering Directives in Vue.js
The counter increments, but the value displayed in the input field does
not reflect this change.
The use of {{count}} in the value attribute does not update the content of
the input field, which remains fixed with the string "{{count}}". To initialize
and update the value attribute of the input field with the value of the reactive
variable count, a directive called v-bind must be used. The v-bind directive
allows binding the value of an attribute to that of a reactive variable.
Therefore, one would write <input type="text"
v-bind:value="count" />, which binds the value attribute of the input
field to the value of a reactive variable, in this case, the count variable.
The template of the MyCounter component becomes the following:
Display the count variable in the value attribute of an input field (file
src/components/MyCounter.vue)
<script setup>
98
Chapter 2 Day 2: Mastering Directives in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br/>
Input : <input type="text" v-bind:value="count" />
<br/><br/>
<button @click="increment()">count+1</button>
</template>
Figure 2-4. Reactive variable in the value attribute with the v-bind
directive
99
Chapter 2 Day 2: Mastering Directives in Vue.js
The input field is now initialized with the value of the reactive variable
count, which is 0.
As the count variable is reactive, incrementing it causes the update of
its display wherever it is used, including in the input field.
Let’s click the “count+1” button several times:
The value displayed in the input field is updated to reflect the value of
the reactive variable count.
The v-bind directive is commonly used in templates. For this reason,
Vue.js allows simplifying the syntax by writing :value="count" instead of
v-bind:value="count".
We had already used this simplified form of the v-bind directive by
writing :init="10" or :init="init" in the previous pages.
One could also write v-bind:value="count+3" because the value
"count+3" is a JavaScript expression interpreted by v-bind.
Additionally, one can write the shorthand form :value="count+3",
which is equivalent to v-bind:value="count+3".
100
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<MyCounter :count="count" :doubleCount="doubleCount" />
<br /><br />
<button @click="increment()">count+1</button>
</template>
101
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br/>
Input : <input type="text" v-bind:value="count" />
</template>
102
Chapter 2 Day 2: Mastering Directives in Vue.js
103
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
onUpdated(() => {
console.log("updated : count = ", props.count);
})
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br/>
Input : <input type="text" v-bind:value="count" />
</template>
104
Chapter 2 Day 2: Mastering Directives in Vue.js
Through this example, we can observe that updating at least one of the
attributes of a component refreshes the entire component.
105
Chapter 2 Day 2: Mastering Directives in Vue.js
Let’s use the example of the previous counter, adding a Start button to
start the counter. Once the counter is started by clicking the Start button,
the Start button is replaced by the Stop button, which allows stopping the
counter. Therefore, the counter is started or stopped (depending on its
state) by alternately clicking on the displayed Start or Stop button.
The v-if and v-else directives will allow us to alternately display the
Start button or the Stop button:
<script setup>
</script>
<template>
<MyCounter />
</template>
106
Chapter 2 Day 2: Mastering Directives in Vue.js
MyCounter component with alternated Start and Stop buttons (file src/
components/MyCounter.vue)
<script setup>
import { ref, computed } from "vue"
</script>
107
Chapter 2 Day 2: Mastering Directives in Vue.js
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br /><br />
<button v-if="!timer" @click="start()">Start</button>
<button v-else @click="stop()">Stop</button>
</template>
The interesting part is the one written with the v-if and v-else
directives:
This code looks logical and functional. Let’s try to see the result
obtained.
When the program is launched, the counter is at 0, and the Start button
is displayed. This corresponds to the executed v-if directive.
108
Chapter 2 Day 2: Mastering Directives in Vue.js
Let’s click the Start button. The counter starts, and the Stop button is
displayed:
109
Chapter 2 Day 2: Mastering Directives in Vue.js
Clicking the Stop button has stopped the counter. However, the button
label is still “Stop” when it should be “Start.” Additionally, clicking the
button again does not restart the counter, which remains stuck at the
stopped value.
So there is an issue. Let’s explain why and resolve it now.
110
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br /><br />
<button v-if="!timer" @click="start()">Start</button>
<button v-else @click="stop()">Stop</button>
</template>
111
Chapter 2 Day 2: Mastering Directives in Vue.js
After stopping the counter, the Start button is now visible, and the
counter can restart:
112
Chapter 2 Day 2: Mastering Directives in Vue.js
v-show Directive
The v-show directive is similar to the v-if directive. The difference is that
v-if inserts the element into the page if the condition specified in the
directive is true whereas v-show inserts it in all cases but only displays it if
the condition is true (the v-show directive uses the element’s style to hide
or show it as needed).
Using the v-show directive in the previous template, we write the
following:
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
<br /><br />
<button v-show="!timer" @click="start()">Start</button>
<button v-show="timer" @click="stop()">Stop</button>
</template>
113
Chapter 2 Day 2: Mastering Directives in Vue.js
v-for Directive
The v-for directive allows for looping, enabling the insertion of the
directive-containing element into the HTML page multiple times.
The value of the v-for directive can be written in several ways,
depending on the need:
Let’s start by studying the writing form v-for="i in n", which allows
for a loop from 1 to the value n.
114
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
</template>
<script setup>
defineProps(["nb"]);
</script>
<template>
</template>
115
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br />
Computed variable doubleCount : <b>{{ doubleCount }}</b>
116
Chapter 2 Day 2: Mastering Directives in Vue.js
</template>
117
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
defineProps(["nb"]);
</script>
118
Chapter 2 Day 2: Mastering Directives in Vue.js
<template>
</template>
119
Chapter 2 Day 2: Mastering Directives in Vue.js
120
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
defineProps(["nb"]);
</script>
<template>
</template>
<script setup>
121
Chapter 2 Day 2: Mastering Directives in Vue.js
defineProps(["index"]);
</script>
<template>
</template>
122
Chapter 2 Day 2: Mastering Directives in Vue.js
123
Chapter 2 Day 2: Mastering Directives in Vue.js
We have previously seen how to use the v-for directive in the form
v-for="i in n", which allows looping from 1 to n. Now, let’s explore how
to loop through an array of elements using another form of the v-for
directive, namely, v-for="(item, i) in items".
124
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
const limits = [
{init: 0, end: 10},
{init: 5},
{end: 10}
];
</script>
<template>
</template>
125
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
defineProps(["limits"]);
</script>
<template>
</template>
126
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
import { ref, computed, defineProps } from "vue"
</script>
127
Chapter 2 Day 2: Mastering Directives in Vue.js
<template>
</template>
128
Chapter 2 Day 2: Mastering Directives in Vue.js
Each counter now has the init and end attributes displayed, retrieved
from the limit attribute of the MyCounter component.
v-model Directive
The v-bind directive we studied earlier allows updating an attribute
associated with a reactive variable when that variable is modified. For
example, the instruction <input v-bind:value="count" /> updates
129
Chapter 2 Day 2: Mastering Directives in Vue.js
the value attribute of the input field when the reactive variable count is
changed. Therefore, the content of the input field is automatically updated.
Conversely, modifying the value attribute of the input field does not
update the associated reactive variable count. To achieve this, the v-model
directive is used.
The v-model directive enables two-way binding (attribute to variable
and variable to attribute), while the v-bind directive allows modification in
only one direction (variable to attribute).
<script setup>
</script>
<template>
<MyCounter />
</template>
130
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
import { ref, computed } from "vue"
</script>
<template>
</template>
Indeed, to use the v-bind directive, you can simplify the syntax by
writing :value="count" instead of v-bind:value="count". During
program execution, the value of the reactive variable (here, 0) initializes
the content of both input fields. This is achieved through the functionality
of the v-bind directive, and it’s worth noting that the v-model directive also
incorporates the behavior of v-bind.
131
Chapter 2 Day 2: Mastering Directives in Vue.js
132
Chapter 2 Day 2: Mastering Directives in Vue.js
133
Chapter 2 Day 2: Mastering Directives in Vue.js
134
Chapter 2 Day 2: Mastering Directives in Vue.js
The advantage of using the v-model directive with the fields in this
form is that modifying each field immediately updates the reactive variable
associated with that field. The values of the reactive variables associated
with each field are displayed below the form.
The fact that each form field is linked to a reactive variable through
v-model allows for retrieving the current input or selection in the form.
Let’s explore how to manage each form field based on its type (input field,
selection list, radio buttons, or check boxes).
135
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<MyForm />
</template>
<script setup>
</script>
<template>
<h3>Input Form</h3>
Name: <input type="text" v-model="name" />
136
Chapter 2 Day 2: Mastering Directives in Vue.js
<br/><br/>
<h3>Reactive Variables</h3>
name: <b>{{name}}</b>
<br/><br/>
</template>
<style scoped>
h3 {
background-color: gainsboro;
padding: 5px;
}
</style>
137
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<h3>Input Form</h3>
Name: <input type="text" v-model="name" />
<br/><br/>
Date of Birth:
<select v-model="birthdate" >
<option v-for="date in dates" :value="date"
:key="date">{{date}}</option>
</select>
<br/><br/>
<h3>Reactive Variables</h3>
name: <b>{{name}}</b>
<br/><br/>
birthdate: <b>{{birthdate}}</b>
138
Chapter 2 Day 2: Mastering Directives in Vue.js
<br/><br/>
</template>
<style scoped>
h3 {
background-color: gainsboro;
padding: 5px;
}
</style>
139
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<h3>Input Form</h3>
Name: <input type="text" v-model="name" />
<br/><br/>
Date of Birth:
<select v-model="birthdate" >
<option v-for="date in dates" :value="date" :key="date">
{{date}}</option>
</select>
<br/><br/>
Marital Status:
140
Chapter 2 Day 2: Mastering Directives in Vue.js
<h3>Reactive Variables</h3>
name: <b>{{name}}</b>
<br/><br/>
birthdate: <b>{{birthdate}}</b>
<br/><br/>
maritalStatus: <b>{{maritalStatus}}</b>
<br/><br/>
</template>
<style scoped>
h3 {
background-color: gainsboro;
padding: 5px;
}
</style>
141
Chapter 2 Day 2: Mastering Directives in Vue.js
142
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
<template>
<h3>Input Form</h3>
Name: <input type="text" v-model="name" />
<br/><br/>
Date of Birth:
<select v-model="birthdate" >
<option v-for="date in dates" :value="date" :key="date">
{{date}}</option>
</select>
<br/><br/>
Marital Status:
<input type="radio" value="M" id="maried"
v-model="maritalStatus">
<label for="maried">Married</label>
<input type="radio" value="S" id="single"
v-model="maritalStatus">
143
Chapter 2 Day 2: Mastering Directives in Vue.js
<label for="single">Single</label>
<input type="radio" value="D" id="divorced"
v-model="maritalStatus">
<label for="divorced">Divorced</label>
<input type="radio" value="W" id="widower"
v-model="maritalStatus">
<label for="widower">Widowed</label>
<br/><br/>
<input type="checkbox" id="read" value="read"
v-model="acceptConditions" />
<label for="read">I have read the terms of use.</label>
<br/><br/>
<input type="checkbox" id="accept" value="accept"
v-model="acceptConditions" />
<label for="accept">I accept the general terms and conditions
of sale.</label>
<br/><br/>
<h3>Reactive Variables</h3>
name: <b>{{name}}</b>
<br/><br/>
birthdate: <b>{{birthdate}}</b>
<br/><br/>
maritalStatus: <b>{{maritalStatus}}</b>
<br/><br/>
acceptConditions: <b>{{acceptConditions}}</b>
<br/><br/>
</template>
<style scoped>
h3 {
144
Chapter 2 Day 2: Mastering Directives in Vue.js
background-color: gainsboro;
padding: 5px;
}
</style>
145
Chapter 2 Day 2: Mastering Directives in Vue.js
<script setup>
</script>
146
Chapter 2 Day 2: Mastering Directives in Vue.js
<template>
<h3>Input Form</h3>
Name: <input type="text" v-model.lazy="name" />
<br/><br/>
<h3>Reactive Variables</h3>
name: <b>{{name}}</b>
<br/><br/>
</template>
<style scoped>
h3 {
background-color: gainsboro;
padding: 5px;
}
</style>
147
Chapter 2 Day 2: Mastering Directives in Vue.js
Conclusion
This chapter has provided us with an in-depth understanding of directives
in Vue.js, ranging from using attributes to mastering directives like v-bind,
v-if, and v-model. These skills are fundamental for building robust and
maintainable Vue.js applications.
The next chapter will guide us through handling DOM events, a crucial
skill to enhance interactivity in Vue.js applications.
148
CHAPTER 3
Day 3: Mastering
Events in Vue.js
In this third day of our Vue.js study, we will focus on event handling, a
crucial element for creating interactive applications with Vue.js.
You will learn how to intercept events related to user actions in the
HTML page and use these events to enable components to exchange
information. This skill is crucial for increasing the reactivity and
interactivity of your Vue.js applications.
Intercepting Events
Vue.js allows you to perform a process when an event occurs using the
v-on directive. To specify the event you want to handle, write its name after
v-on, preceded by “:”. For example, you write v-on:click to indicate that
you want to handle the click event.
To handle a click on a button, you write the following:
<button v-on:click="increment()">Increment</button>
<button @click="increment()">Increment</button>
<script setup>
</script>
<template>
<MyCounter />
</template>
<script setup>
</script>
<template>
150
Chapter 3 Day 3: Mastering Events in Vue.js
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{count}}</b>
<br/><br/>
<button v-on:click="increment()">count+1</button>
</template>
After several clicks on the button, the reactive variable count has been
incremented:
<script setup>
151
Chapter 3 Day 3: Mastering Events in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{count}}</b>
<br/><br/>
<button v-on:click="increment(2)">count+1</button>
</template>
152
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
153
Chapter 3 Day 3: Mastering Events in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
154
Chapter 3 Day 3: Mastering Events in Vue.js
155
Chapter 3 Day 3: Mastering Events in Vue.js
We use the input event for this purpose, which is triggered when
the key has been processed. If the value entered in the field is greater
than 100, we display an error message using a reactive variable message
initialized to “”.
Do not exceed the value 100 in the input field (file src/components/
MyCounter.vue)
<script setup>
156
Chapter 3 Day 3: Mastering Events in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
During the input event, the value entered in the input field is available
in the variable event.target.value. If this value is greater than 100, the
reactive variable message is initialized with the text of the error message to
be displayed; otherwise, the message is cleared.
For example, if you enter the value 101 in the input field, you can see
the error message as shown in Figure 3-4.
157
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
158
Chapter 3 Day 3: Mastering Events in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
159
Chapter 3 Day 3: Mastering Events in Vue.js
160
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
</script>
<template>
</template>
<script setup>
import MyCounter from "./MyCounter.vue";
import { defineProps } from "vue";
161
Chapter 3 Day 3: Mastering Events in Vue.js
</script>
<template>
</template>
<script setup>
</script>
<template>
162
Chapter 3 Day 3: Mastering Events in Vue.js
<br/><br/>
<button @click="increment()">count+1</button>
<br/>
</template>
In Figure 3-6, you can see the display of the three counters.
163
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
import MyCounter from "./MyCounter.vue";
import { ref, defineProps } from "vue";
</script>
<template>
</template>
164
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
</script>
<template>
165
Chapter 3 Day 3: Mastering Events in Vue.js
<button @click="increment()">count+1</button>
<br/>
</template>
167
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
import MyCounters from "./components/MyCounters.vue"
</script>
<template>
</template>
168
Chapter 3 Day 3: Mastering Events in Vue.js
<script setup>
</script>
<template>
</template>
<script setup>
import { ref, defineProps, inject } from "vue"
169
Chapter 3 Day 3: Mastering Events in Vue.js
</script>
<template>
</template>
170
Chapter 3 Day 3: Mastering Events in Vue.js
Conclusion
At this point, you’ve gained a solid understanding of the basics of Vue.
js, including state and event management, directives, and components.
However, modern web applications often don’t operate in isolation; they
frequently interact with remote servers to retrieve or send data.
171
Chapter 3 Day 3: Mastering Events in Vue.js
In the next chapter, we’ll take a crucial step forward by exploring how
to make HTTP requests to communicate with a remote server. We’ll put
this knowledge into practice by building an application that retrieves and
displays a list of all countries worldwide using a REST API. You’ll also learn
how to filter this data based on user input.
172
CHAPTER 4
Day 4: Building an
Application in Vue.js
In this chapter, we’ll delve into a fundamental aspect of modern web
development: communication between the client and server via HTTP
requests. You’ll learn how to make requests to retrieve data from a remote
server and use it in your Vue.js application.
We’ll build a practical application to implement these concepts. The
application will display a list of all countries worldwide, allowing the
user to filter this list based on the characters entered in a search field.
To achieve this, we’ll use the REST API available at the following URL:
https://restcountries.com/v3.1/all.
So not only will you reinforce your Vue.js skills, but you’ll also acquire
new skills in JSON data manipulation and interacting with REST APIs.
Application Screens
Here, we describe all the screens of the application, based on user actions.
Upon launching the application, the list of countries is retrieved
(Figure 4-2) and displayed (Figure 4-3):
174
Chapter 4 Day 4: Building an Application in Vue.js
175
Chapter 4 Day 4: Building an Application in Vue.js
176
Chapter 4 Day 4: Building an Application in Vue.js
177
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
</script>
<template>
</template>
178
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
</script>
<template>
<h3>Country List</h3>
</template>
179
Chapter 4 Day 4: Building an Application in Vue.js
180
Chapter 4 Day 4: Building an Application in Vue.js
181
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
onMounted(() => {
var url = "https://restcountries.com/v3.1/all";
fetch(url)
.then((res) => res.text())
.then((data) => {
countries = JSON.parse(data).map(function(elem) {
return elem.name.common;
});
// In ascending alphabetical order
countries = countries.sort((n1, n2) => {
if (n1 > n2) return 1;
if (n1 < n2) return -1;
return 0;
182
Chapter 4 Day 4: Building an Application in Vue.js
});
names.value = countries; // Updating the displayed list.
})
.catch((err) => names.value = [err.toString()]);
});
</script>
<template>
<h3>Country List:</h3>
</template>
183
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
184
Chapter 4 Day 4: Building an Application in Vue.js
onMounted(async () => {
names.value = await getCountries();
});
</script>
<template>
<h3>Country List:</h3>
</template>
185
Chapter 4 Day 4: Building an Application in Vue.js
186
Chapter 4 Day 4: Building an Application in Vue.js
To filter the list of countries based on the entered name, you would
need to execute the following code block:
187
Chapter 4 Day 4: Building an Application in Vue.js
188
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
onMounted(() => {
var url = "https://restcountries.com/v3.1/all";
fetch(url)
.then((res) => res.text())
.then((data) => {
countries = JSON.parse(data).map(function(elem) {
return elem.name.common;
});
// In ascending alphabetical order
countries = countries.sort((n1, n2) => {
if (n1 > n2) return 1;
if (n1 < n2) return -1;
return 0;
});
names.value = countries;
})
.catch((err) => names.value = [err.toString()]);
});
});
names.value = countriesFiltered;
});
</script>
<template>
<h3>Country List:</h3>
</template>
Indeed, it’s crucial to test the functionality. Let’s run the program and
enter characters in the input field to verify its operation.
190
Chapter 4 Day 4: Building an Application in Vue.js
191
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
onMounted(() => {
var url = "https://restcountries.com/v3.1/all";
fetch(url)
.then((res) => res.text())
.then((data) => {
countries = JSON.parse(data).map(function(elem) {
return elem.name.common;
});
// In ascending alphabetical order
countries = countries.sort((n1, n2) => {
if (n1 > n2) return 1;
if (n1 < n2) return -1;
return 0;
});
names.value = countries;
})
.catch((err) => names.value = [err.toString()]);
});
192
Chapter 4 Day 4: Building an Application in Vue.js
</script>
<template>
<h3>Country List:</h3>
</template>
193
Chapter 4 Day 4: Building an Application in Vue.js
194
Chapter 4 Day 4: Building an Application in Vue.js
195
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
onMounted(() => {
var url = "https://restcountries.com/v3.1/all";
fetch(url)
.then((res) => res.text())
.then((data) => {
countries = JSON.parse(data).map(function(elem) {
return elem.name.common;
});
// In ascending alphabetical order
countries = countries.sort((n1, n2) => {
if (n1 > n2) return 1;
if (n1 < n2) return -1;
return 0;
});
names.value = countries;
})
.catch((err) => names.value = [err.toString()]);
});
watchEffect(() => {
let countriesFiltered = countries.filter((n) => {
196
Chapter 4 Day 4: Building an Application in Vue.js
</script>
<template>
<h3>Country List:</h3>
</template>
One can verify that this does not work, and it is expected, as for
the variable props.name to be within a code block executed during
the component initialization, the filter() method would need to be
executed, which is not the case here because the countries array is
initially empty.
A workaround involves the inclusion of the statement console.
log(props.name), effectively allowing the utilization of the variable props.
name for subsequent observation. The modification to the watchEffect()
method is as follows:
watchEffect(() => {
console.log(props.name); // Do not delete: allows the
observation of props.name
197
Chapter 4 Day 4: Building an Application in Vue.js
198
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
</script>
<template>
</template>
199
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
onMounted(() => {
var url = "https://restcountries.com/v3.1/all";
fetch(url)
.then((res) => res.text())
.then((data) => {
countries = JSON.parse(data).map(function(elem) {
return elem.name.common;
});
// In ascending alphabetical order
countries = countries.sort((n1, n2) => {
if (n1 > n2) return 1;
if (n1 < n2) return -1;
return 0;
});
200
Chapter 4 Day 4: Building an Application in Vue.js
names.value = countries;
})
.catch((err) => names.value = [err.toString()]);
});
watch(name, () => {
let countriesFiltered = countries.filter((n) => {
const reg = new RegExp("^" + name.value, "i");
if (n.match(reg)) return true;
else return false;
});
names.value = countriesFiltered;
});
</script>
<template>
<h3>Country List:</h3>
</template>
201
Chapter 4 Day 4: Building an Application in Vue.js
202
Chapter 4 Day 4: Building an Application in Vue.js
<script setup>
onMounted(()=>document.querySelector("input[type=text]").
focus());
</script>
<template>
</template>
The input field is accessed through the DOM API, for example, by
using document.querySelector(selector). All that remains is to use the
focus() method of the DOM API on this element to give it focus.
203
Chapter 4 Day 4: Building an Application in Vue.js
In the next chapter, we will explore the creation of a new directive that
directly gives focus to the input field, providing a more optimal approach.
Conclusion
You now have a comprehensive understanding of how to perform HTTP
requests in a Vue.js application. You have learned to retrieve data from
a remote server and seamlessly integrate it into your application. The
example of the country list has allowed you to implement these concepts,
providing you with a practical and concrete experience.
204
Chapter 4 Day 4: Building an Application in Vue.js
205
CHAPTER 5
Directive Lifecycle
A Vue.js directive is a JavaScript object that can have one or more of the
following lifecycle properties. Each property is a method called at different
stages of the lifecycle of the DOM element to which the directive is applied.
This allows for the execution of specific actions at each stage:
208
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
209
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
210
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const focusDirective = {
mounted(el) {
el.focus();
}
};
app.mount('#app');
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Counter value : <input v-focus />
</template>
211
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<MyCounter />
</template>
Let’s launch the application. The input field gains focus automatically
without the need for a click.
We have seen how to insert the new directive directly into the src/
main.js file. However, it is more practical to use another file to avoid
modifying, for each added directive, this crucial file for the application’s
operation.
212
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const focusDirective = {
mounted(el) {
el.focus();
}
};
export default {
focus : focusDirective,
}
213
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
app.mount('#app');
const focusDirective = {
mounted(el) {
el.focus();
}
214
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
};
export default {
focus,
}
If you want to import other directives, you just need to add them to
the src/directives.js file in the same manner. The other files, App.
vue and MyCounter.vue, remain identical to the previous versions. The
functionality remains unchanged.
215
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
Let’s see how to write these different forms of using the v-integers-
only directive.
216
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
217
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<br/><br/>
Entered value: <b>{{count}}</b>
</template>
const integersOnly = {
mounted(el) {
el.addEventListener("keydown", (event) => {
const numbers = ["0", "1", "2", "3", "4", "5", "6", "7",
"8", "9"];
const moves = ["Backspace", "ArrowLeft", "ArrowRight",
"Delete", "Tab", "Home", "End"];
// If the key is not allowed, do not take it into account.
// The event object is available here.
if (!authorized.includes(event.key)) event.preventDefault();
});
},
}
218
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
export default {
focus,
integersOnly, // It will be used in the form of
v-integers-only
}
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
219
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
</template>
Only numeric characters and movement arrows are now allowed in the
input field.
220
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const integersOnly = {
mounted(el, binding) {
el.addEventListener("keydown", (event) => {
const numbers = ["0", "1", "2", "3", "4", "5", "6", "7",
"8", "9"];
const moves = ["Backspace", "ArrowLeft", "ArrowRight",
"Delete", "Tab", "Home", "End"];
const letters = ["a", "b", "c", "d", "e", "f", "A", "B",
"C", "D", "E", "F"];
221
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
222
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<br/><br/>
Entered value: <b>{{count}}</b>
</template>
<script setup>
223
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
Note that we positioned the v-model directive first in the input field.
Indeed, directives are processed by Vue.js in the order they appear in an
element. This allows, first and foremost, to initialize the content of the field
with the value of the associated reactive variable, thanks to v-model. The
content of the field can then be used by the following directives, which
would not be possible if the v-model directive were written at the end.
The v-integers-only directive is modified to account for the upper
modifier.
const integersOnly = {
mounted(el, binding) {
if (binding.modifiers.upper) {
// Convert the displayed field to uppercase with an
initial value
224
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
225
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
if (letters.includes(event.key)) {
const start = el.selectionStart;
const end = el.selectionEnd;
const text = el.value;
226
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
Let’s see how to write these different forms of using the v-max-value
directive.
<script setup>
227
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
const maxValue = {
mounted(el) {
const value = el.value || 0; // Value in the field
if (value > 100) el.style.color = "red";
else el.style.color = "";
},
updated(el) {
const value = el.value || 0; // Value in the field
if (value > 100) el.style.color = "red";
else el.style.color = "";
},
}
228
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const maxValue = {
mounted(el) {
treatment(el);
},
updated(el) {
treatment(el);
},
}
File src/directives.js
229
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
export default {
focus,
integersOnly,
maxValue,
}
Let us verify that this is functional. As soon as the input value exceeds
100, the input field should change its color to red.
230
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
The maximum value of the field is initialized here to the value of 200.
The v-max-value directive is modified to handle the value assigned to it.
231
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const maxValue = {
mounted(el, binding) {
treatment(el, binding);
},
updated(el, binding) {
treatment(el, binding);
},
}
232
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
Let’s see how to use this new form of the directive in the MyCounter
component:
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
233
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
</template>
const maxValue = {
mounted(el, binding) {
treatment(el, binding);
},
234
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
updated(el, binding) {
treatment(el, binding);
},
}
When the maximum value is exceeded, we set the font to “bold” and
also modify it to “Arial” to make the change even more noticeable on
the screen.
The maximum value of 200 having been exceeded, the input field has
turned red and bold.
235
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
236
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
237
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
const focusDirective = {
mounted(el) {
el.focus();
},
};
238
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
239
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<h3>MyCounter Component</h3>
</template>
const focusDirective = {
mounted(el, binding) {
el.focus();
const arg = binding.arg;
const value = binding.value;
if (arg == "color") el.style.color = value;
}
};
The input field gains focus, and the input field’s color changes
to “cyan”.
240
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
Note in this example that the input field retains its new color even if the
input field loses focus. It would be necessary to restore the previous color
of the input field when it loses focus.
To achieve this, we need to handle the focus and blur events on the
input field. Let’s modify the directive for that purpose.
Handling the focus and blur events in the v-focus directive (file src/
directives/focus.js)
const focusDirective = {
mounted(el, binding) {
const arg = binding.arg;
const value = binding.value;
// Position the handling of the focus and blur events
el.addEventListener("focus", () => {
if (arg == "color") el.style.color = value;
});
el.addEventListener("blur", () => {
if (arg == "color") el.style.color = "";
241
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
});
// and then give focus to the input field
el.focus();
}
};
We modify the color of the input field upon entering the field (focus
event) and then restore the initial color upon leaving the field (blur event).
Additionally, we give focus to the input field only after positioning the
event handlers; otherwise, they would not be considered during the initial
display of the component.
Let’s verify that this is functional. Upon launching the program, the
input field gains focus, and the color of the input field is modified:
Then click outside the input field. The color of the input field is
removed.
242
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
This form of the v-focus directive, using the focus and blur events,
will be retained in the following examples.
<script setup>
</script>
243
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<template>
<h3>MyCounter Component</h3>
</template>
const focusDirective = {
mounted(el, binding) {
const arg = binding.arg;
const value = binding.value;
// Position the handling of the focus and blur events
el.addEventListener("focus", () => {
if (arg == "color") el.style.color = value;
if (arg == "backgroundcolor") el.style.backgroundColor
= value;
});
el.addEventListener("blur", () => {
if (arg == "color") el.style.color = "";
if (arg == "backgroundcolor") el.style.
backgroundColor = "";
});
244
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
245
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
const focusDirective = {
mounted(el, binding) {
const arg = binding.arg;
const value = binding.value;
// Position the handling of the focus and blur events
el.addEventListener("focus", () => {
if (arg == "color") el.style.color = value;
246
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
The MyCounter component now takes into account a new text and
background color for the input field:
247
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
248
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
</template>
Clicking the Clear button clears the content of the input field:
249
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const clearable = {
mounted(el) {
const clearButton = document.createElement("button");
clearButton.innerHTML = "Clear";
clearButton.style = "position:relative; left:10px;";
// Handle the click on the button (clear the content of the
input field).
clearButton.addEventListener("click", () => {
// Clear the content of the input field.
el.value = "";
// Simulate an input event to mimic a keyboard key press
// (mandatory to ensure that the reactive variable linked
to the input field is updated)
el.dispatchEvent(new Event("input"));
// Give focus to the input field
el.focus();
250
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
});
File src/directives.js
export default {
focus,
integersOnly,
maxValue,
clearable,
}
251
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
252
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const timer = {
mounted(el) {
// Initialization of the displayed time
// (allows it to be displayed immediately without waiting
for a second)
let time = getCurrentTime();
el.innerHTML = time;
setInterval(()=>{
// Then incrementing the time every second
let time = getCurrentTime();
el.innerHTML = time;
}, 1000); // Every 1000 milliseconds (1 second).
},
}
function getCurrentTime() {
// Return the current time in the format HH:MM:SS
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
export default {
focus,
integersOnly,
maxValue,
clearable,
timer,
}
254
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
const timer = {
mounted(el, binding) {
const ms = binding.modifiers.ms;
let time = getCurrentTime(ms);
el.innerHTML = time;
setInterval(()=>{
let time = getCurrentTime(ms);
el.innerHTML = time;
}, 100);
},
}
if (ms) {
const milliseconds = now.getMilliseconds().toString().
slice(0, 1); // Obtaining tenths of a second
formattedTime += `.${milliseconds}`;
}
255
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
return formattedTime;
}
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
256
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
The first timer displays the time every second, while the second
displays the time every tenth of a second.
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
257
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
</template>
The ms modifier can be used with the chrono modifier to add more
precision to the elapsed time. The processing of the chrono modifier is
inserted into the v-timer directive file:
const timer = {
mounted(el, binding) {
const ms = binding.modifiers.ms;
const chrono = binding.modifiers.chrono;
setInterval(()=>{
if (!chrono) {
let time = getCurrentTime(ms);
el.innerHTML = time;
}
else {
const chronoTime = getChronoTime(ms);
258
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
el.innerHTML = chronoTime;
}
}, 100);
},
}
if (ms) {
const milliseconds = now.getMilliseconds().toString().
slice(0, 1); // Obtaining tenths of a second
formattedTime += `.${milliseconds}`;
}
return formattedTime;
}
259
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
if (ms) {
const milliseconds = Math.floor(remainingMilliseconds2
% 1000);
const tenthsOfSecond = Math.floor((milliseconds %
1000) / 100);
formattedTime += `.${tenthsOfSecond.toString()}`;
}
return formattedTime;
}
260
Chapter 5 Day 5: Mastering the Creation of Directives in Vue.js
Conclusion
We have explained how to create new directives in Vue.js, simplifying the
code of our Vue.js components. Thanks to arguments and modifiers, Vue.
js directives offer incredible functionalities that allow simple and intuitive
use in components.
261
CHAPTER 6
Day 6: Mastering
the Creation
of Composables
in Vue.js
As seen in the previous chapter, creating new directives makes it easier to
manipulate the HTML elements on which the directives are used. In this
chapter, we will explore composables. Composables are also known as
composition functions.
Composables defined in our Vue.js components are used to
encapsulate application logic in the form of reusable functions. Thus, they
are oriented toward internal component logic and data management.
264
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
265
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
</script>
266
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br /><br />
<button @click="increment">count+1</button>
<button @click="decrement">count-1</button>
</template>
267
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count : <b>{{ count }}</b>
<br /><br />
<button @click="increment">count+1</button>
<button @click="decrement">count-1</button>
</template>
268
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
269
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
270
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
Usage As an Object
If we use return {count, increment, decrement} in the
composable, the names used in the returned object cannot be changed
during their usage. However, the order of properties in the object no longer
matters.
Thus, in the MyCounter component, we could write the following:
The order of the returned elements is changed, but not their names
because the names of object properties are not modifiable, while their
order of appearance does not matter. Therefore, an object {key1, key2} is
equivalent to an object {key2, key1}.
Thus, a composable can return data as an array or as an object, with
the constraints mentioned previously. However, in the examples that
follow, it will be seen that it is more judicious to return data as an array
rather than as an object.
271
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
const init = 1;
const max = 5;
const [count, increment, decrement] = useCounterMax(init, max);
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count: <b>{{ count }}</b>
<br><br>
Maximum value: <b>{{max}}</b>
272
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
</template>
273
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
increment(); // Increment
}
}
return [count, incrementMax, decrement];
}
274
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
tep 2: useCounterMaxWithError( )
S
Composable
Let’s enhance the useCounterMax() composable by allowing it to display
an error message if attempting to exceed the maximum value.
This will be the role of the new useCounterMaxWithError()
composable, which displays an error message in this case.
<script setup>
const init = 1;
const max = 5;
const [count, increment, decrement, error] =
useCounterMaxWithError(init, max);
</script>
<template>
<h3>MyCounter Component</h3>
Reactive variable count: <b>{{ count }}</b>
<br><br>
Maximum value: <b>{{max}}</b>
275
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<br><br>
Error: <b>{{error}}</b>
</template>
276
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
}
return [count, incrementMax, decrementMax, error];
}
277
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
Figure 6-4. Initial counter value higher than the maximum value: no
error displayed
To remedy this, the composable should test the values passed by the
component during its initialization. A composable has access to all the
functionalities of a component, particularly its lifecycle through methods
such as onMounted(), onUpdated(), etc. These are the lifecycle methods of
a Vue.js component that were described in Chapter 1.
278
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
Let’s use the onMounted() method, which allows for processing when
the component is mounted in the DOM.
The useCounterMaxWithError() composable becomes the following:
279
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
Let us verify that the error is now displayed when the initial value of
the counter exceeds the maximum value:
Utility Composables
Following the same principle as the useCounter() composable (and its
derived composables), let’s write other composables that will serve as
utility composables, as they can be used in various applications. These
include the following:
1. useFetch(): Performs an HTTP request.
2. useWindowSize(): Determines the size of the window.
3. useGeolocation(): Determines the current
geolocation.
280
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
import useFetch from "../composables/useFetch"
</script>
281
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<template>
</template>
282
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
return [startFetch];
}
<script setup>
</script>
<template>
<MyCountries />
</template>
283
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
Clicking on the “Start Fetch” button retrieves the list of countries from
the server and displays it as a table in the reactive data variable.
284
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
import useFetchCountries from "../composables/
useFetchCountries"
</script>
<template>
</template>
Note that the name of the function returned by the composable can
remain the same as before, as only the position in the returned array is
relevant, as we have previously explained.
The new useFetchCountries(url) composable is written as follows:
286
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
import useFetchCountries from "../composables/
useFetchCountries"
</script>
<template>
</template>
useWindowSize( ) Composable
for Real-Time Window Size Information
The useWindowSize() composable returns a reactive variable, windowSize,
represented by an object { width, height }, indicating the width and
height of the window in which the application is running, respectively.
288
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
onMounted(() => {
window.addEventListener('resize', updateWindowSize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', updateWindowSize);
});
return windowSize;
}
289
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
<script setup>
</script>
<template>
<MyCounter />
</template>
290
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
291
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
onMounted(() => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(handleGeolocati
on, errorGeolocation);
} else {
console.log("Geolocation is not available in this
browser.");
}
});
292
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Latitude: <b>{{latitude}}</b>
<br>
Longitude: <b>{{longitude}}</b>
<br>
</template>
293
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
useGeolocationWithDetails( ) Composable
Derived from useGeolocation( )
Let’s enhance the previous useGeolocation() composable by providing
not only the latitude and longitude but also the corresponding
country and city. For this purpose, we create the new composable
useGeolocationWithDetails().
To achieve this, we use a new API from the site https://nominatim.
openstreetmap.org/reverse. By specifying latitude and longitude in
the URL, it returns a JSON object containing, among other things, the
corresponding country and city.
For example, using the following URL in a browser: https://
nominatim.openstreetmap.org/reverse?format=json&lat=30.267153&l
on=-97.7430608.
294
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
295
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
Latitude : <b>{{latitude}}</b>
<br>
Longitude : <b>{{longitude}}</b>
<br>
Country : <b>{{country}}</b>
<br>
City : <b>{{city}}</b>
<br>
</template>
296
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
297
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
The country and city names are also displayed in the component.
298
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
299
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-
scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
300
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
To include Leaflet’s CSS styles, you can use the <link> tag in the
application.
Now, let’s explore how to use Leaflet in the useMap() composable.
301
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
302
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
</script>
<template>
<h3>MyCounter Component</h3>
</template>
<style scoped>
#map {
height: 300px;
width: 100%;
}
</style>
303
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
304
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
305
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
<script setup>
</script>
<template>
<h3>MyCounter Component</h3>
</template>
<style scoped>
#map {
height: 300px;
width: 100%;
}
</style>
<div v-map="{latitude:latitude,longitude:longitude}"
id="map" />
306
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
This <div> element now uses the v-map directive, to which we pass
the latitude and longitude returned by useGeolocationWithDetails()
as its value. The v-map directive internally retrieves these values using
the binding parameter through binding.value.latitude and binding.
value.longitude.
It is worth noting that the component will be automatically
refreshed when the v-map directive has its value changed, thanks to
the reactive variables. Therefore, the map will be displayed with the
final values of latitude and longitude when they are obtained by the
useGeolocationWithDetails() composable.
The v-map directive utilizes the useMap() composable. It is written as
follows:
const map = {
updated(el, binding) {
const latitude = binding.value.latitude;
const longitude = binding.value.longitude;
if (latitude && longitude) {
if (el._map) el._map.remove();
el._map = useMap(latitude, longitude, el.id);
}
else if (el._map) {
el._map.remove();
el._map = null;
}
}
}
307
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
export default {
focus,
integersOnly,
maxValue,
clearable,
timer,
map,
}
308
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
Conclusion
In this chapter, we explored in detail the creation and usage of
composables in Vue.js. We discussed why composables are valuable and
how they differ from directives, being complementary. We then followed
the steps to create and enhance Vue.js composables, illustrating this
through concrete examples.
We also discovered utility composables that facilitate the management
of common tasks such as HTTP requests, monitoring window size,
geolocation, and displaying maps.
309
Chapter 6 Day 6: Mastering the Creation of Composables in Vue.js
One of the most interesting examples was the creation of the useMap()
composable to display maps based on GPS location using the Leaflet
API. We followed a step-by-step process to create this composable, gaining
insights into how to integrate third-party libraries into Vue.js.
Finally, we saw how to use the composables we created by
incorporating them into custom directives, such as v-map, to make their
usage even more user-friendly.
In conclusion, composables are a powerful way to abstract reusable
logic and simplify the development of Vue.js applications. They provide
an elegant way to handle complexity while keeping the code clean and
modular. By mastering the creation and usage of composables, you can
significantly enhance your productivity in Vue.js development. The ball is
in your court!
310
CHAPTER 7
JavaScript Reminders
Before delving into the world of Vue.js, it is important to recall the
fundamentals of JavaScript. This section is intended to assist you in
refreshing your memory and getting up to speed on the key concepts of
JavaScript that are utilized throughout the book.
We will cover essential concepts such as variables, arrays, objects,
arrow functions, and modules. Additionally, we will explore more
advanced concepts like asynchronous functions.
The var keyword has a function scope, meaning that the variable is
accessible inside the function where it is declared, as well as anywhere
inside that function.
For example:
function example() {
var x = 10;
if (true) {
var x = 20; // The variable x is accessible inside the
function example()
}
console.log(x); // Displays 20
}
function example() {
let x = 10;
if (true) {
let x = 20; // The variable x is accessible only within the
"if" block
312
Chapter 7 JavaScript Reminders
}
console.log(x); // Displays 10
}
const pi = 3.14159;
313
Chapter 7 JavaScript Reminders
Value Immutability
const pi = 3.14159;
pi = 3.14; // This will result in an error
Block Scope
if (true) {
const message = "Hello";
console.log(message); // OK
}
console.log(message); // Error: 'message' is not defined
You cannot declare another variable with the same name in the same
scope if you’ve already declared it with const.
No Redeclaration
When using const with objects and arrays, the reference to the object
or array itself is immutable, but the properties or elements within the
object or array can still be modified.
314
Chapter 7 JavaScript Reminders
const person = {
name: "Gaby",
age: 40,
city: "Austin"
};
315
Chapter 7 JavaScript Reminders
In this example, we create two variables, name and age, that correspond
to properties of the same names in the person object. We can now use
these variables independently of the rest of the object.
Destructuring can also be used to pass arguments to a function in a
more concise way. For example, you can create a function that takes an
object person as an argument and displays the person’s name and age:
displayNameAge(person);
This will display “The name is Gaby and the age is 40” in the console.
In summary, object structuring and destructuring in JavaScript
are powerful techniques that allow for efficient and concise data
manipulation.
316
Chapter 7 JavaScript Reminders
const person = {
name: "Gaby",
age: 40,
city: "Austin",
profession: "Developer",
};
const object1 = { x: 1, y: 2 };
const object2 = { ...object1 };
console.log(object2); // { x: 1, y: 2 }
const object1 = { x: 1, y: 2 };
const object2 = { z: 3 };
318
Chapter 7 JavaScript Reminders
319
Chapter 7 JavaScript Reminders
File: myModule.js
321
Chapter 7 JavaScript Reminders
File: test.js
console.log(myVariable);
myFunction();
const myInstance = new MyClass();
You can also write the import statement on a single line as follows:
File: test.js
console.log(myVariable);
myFunction();
const myInstance = new MyClass();
This way, we have access to the exported elements from the myModule.
js file in the test.js file.
322
Chapter 7 JavaScript Reminders
File: index.html
<!DOCTYPE html>
<html>
<head>
<title>Example of Using Modules in HTML</title>
</head>
<body>
<script type="module">
import { myFunction } from './myModule.js';
myFunction();
</script>
</body>
</html>
323
Chapter 7 JavaScript Reminders
console.log(myVariable);
console.log(myVar1);
console.log(myVar2);
myFunction();
console.log(myModule.myVar1);
console.log(myModule.myVar2);
myModule.myFunction();
324
Chapter 7 JavaScript Reminders
There are two main ways to export elements in JavaScript: using the
export syntax or export default syntax.
The export syntax is used to export named elements. For example, to
export a named variable myVariable and a named function myFunction
from a file myModule.js, you can use the following syntax:
The exported elements can then be imported into another file using
the import statement. Here’s an example:
326
Chapter 7 JavaScript Reminders
console.log("This is my function");
}
327
Chapter 7 JavaScript Reminders
In this example, the default exported element (the MyClass class) can
be imported using a name of our choice (MyCustomName in this case). This
can be useful if we want to give a more meaningful name to the imported
element or avoid naming conflicts.
In summary, export is used to export multiple named elements from
a module, while export default is used to export an element that will be
considered the default one during import. The decision to use one or the
other depends on how we want to expose module elements and how we
want to import them into other files.
328
Chapter 7 JavaScript Reminders
329
Chapter 7 JavaScript Reminders
In this example, the first function is a traditional function that finds the
largest number in an array. The second function is the arrow version of the
same function, which uses a more concise syntax. Both functions have the
same functionality, and the arrow version also uses the return statement
to return the calculated value.
Note that the arrow function syntax still includes parentheses around
the parameters and the => arrow, but this time there are also curly braces
to delimit the function body. The return statement is used to return the
calculated value.
330
Chapter 7 JavaScript Reminders
const obj = {
name: "John",
sayHello: function() {
console.log(`Hello, my name is ${this.name}`); // "John"
(this refers to obj)
},
sayHelloArrow: () => {
console.log(`Hello, my name is ${this.name}`); //
undefined (this refers to the parent of obj)
}
}
331
Chapter 7 JavaScript Reminders
When the sayHello() function is called, this refers to the obj object,
allowing access to the name property and displaying it in the console.
However, when the sayHelloArrow() function is called, this refers to
the lexical context in which the function was defined, which is the global
context in this case. Therefore, this.name is undefined in the arrow
function, and undefined is displayed in the console.
Depending on the value of this that we want to access, we will use
either a regular function or an arrow function.
332
Chapter 7 JavaScript Reminders
The map() method then returns a new array with the results of applying
the callback() function to each original element. The resulting array will
have the same length as the original array.
Here’s a simple example for better understanding:
Here, we created an array with numbers, and then we used the map()
method to create a new array with the same numbers, but multiplied by 2.
The map() method returns a new array constructed from the elements
of the original array. The resulting array will have the same number of
elements as the original array.
To reduce the number of elements in the resulting array, we will
use the filter() method, which allows us to select the elements to be
included in the resulting array (but without modifying them). Let’s now
look at the filter() method.
333
Chapter 7 JavaScript Reminders
The filter() method then returns a new array with all the elements of
the original array that satisfy the specified condition.
Here’s a simple example to better understand:
console.log(filteredArray); // [2, 4]
Here, we’ve created an array with numbers, and then we used the
filter() method to create a new array with only the even numbers.
334
Chapter 7 JavaScript Reminders
335
Chapter 7 JavaScript Reminders
The Promise object exposes two methods for handling the results of
the asynchronous task:
336
Chapter 7 JavaScript Reminders
loadImage("https://example.com/image.jpg",
function(error, img) {
if (error) {
console.error(error);
} else {
document.body.appendChild(img);
}
});
This example uses the Image object to load an image from the server.
The loadImage() function takes the image URL and a callback function
as arguments. This callback will be invoked once the image has been
loaded. After the image is loaded, further processing can take place
within the callback function, allowing for handling of the asynchrony. The
callback function has two arguments: a possible error and the loaded img
(image) itself.
function loadImage(url) {
return new Promise(function(resolve, reject) {
const img = new Image();
img.onload = function() {
// no error
resolve(img);
};
337
Chapter 7 JavaScript Reminders
img.onerror = function() {
// error
reject("Unable to load the image.");
};
img.src = url; // image loading
});
}
loadImage("https://example.com/image.jpg")
.then(function(img) {
document.body.appendChild(img);
})
.catch(function(error) {
console.error(error);
});
338
Chapter 7 JavaScript Reminders
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
339
Chapter 7 JavaScript Reminders
console.error(error);
}
}
getData();
340
Chapter 7 JavaScript Reminders
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
myFunction();
This myFunction() function uses the await statement to wait for the
resolution of the Promise object returned by the wait() function. The
function prints “Start” to the console, waits for two seconds using await
wait(2000), and then prints “End” to the console.
341
Chapter 7 JavaScript Reminders
In cases where the wait(ms) function wants to return a value, you can
simply provide that value (“value”) as a parameter in the resolve(value)
function call.
The wait(ms) function becomes the following:
function wait(ms) {
return new Promise(resolve => setTimeout(() =>
resolve("Waiting for " + ms + " ms"), ms));
}
myFunction();
342
Chapter 7 JavaScript Reminders
function wait(ms) {
return new Promise(resolve => setTimeout(() =>
resolve("Waiting for " + ms + " ms"), ms));
}
myMainFunction();
343
Chapter 7 JavaScript Reminders
function wait(ms) {
console.log("wait(" + ms + ")");
return new Promise(resolve => setTimeout(() =>
resolve("Waiting for " + ms + " ms"), ms));
}
344
Chapter 7 JavaScript Reminders
Conclusion
In this chapter, we have covered the fundamentals of JavaScript, which
are the ones used in programs written with Vue.js. This knowledge is
necessary to harness the full power of Vue.js in our applications.
345
Index
A destructuring, 320
structuring, 319, 320
addEventListener() method, 219
Arrow functions, 328
App component, 24, 29
syntax, 329, 330
HelloWorld, 26, 34
vs. traditional functions, 328
import statement, 35
value of this, 331, 332
MyCounter component, 34, 42,
Asynchronous function
283, 290
await statement
versions, 35
async keyword, 343, 344
app.directive(name, callback)
Promise object, 341, 342
method, 210
cases, 340
Applications
App component, 178, 181
inject() method, 199, 200, 202 B
filter list of countries, 187–189 Backgroundcolor
focus to input field, 202–204 argument, 243–245
MyCountries Bold modifier, 227, 233–235
component, 178–181
provide() method, 199, 200, 202
REST API, 173, 174 C
retrieve list of callback() function, 332, 333
countries, 182–186 catch() methods, 184, 336, 338
screens, list of Components
countries, 174–177 aforementioned methods, 52
watchEffect() method, 194–199 App component, 23, 34 (see also
watch() method, 188–194 App component)
Arrays Composition API syntax, 35
”...” notation, 320, 321 computed properties, 48–50
348
INDEX
MyCountries useMaximum()
component, 281–283 count variable, 72
retrieved countries, 284 creation, 71, 72
startFetch() method, 282 MyCounter
useFetchCountries(), 285 component, 73, 74
countries, HTML format, 288 useMaximum.js file, 72, 73
fetchCountries() uses, 263, 264, 266
function, 284 useUpperCase()
JavaScript array, 287 MyCounter component, 83
MyCountries component, useUpperCase.js file, 82, 83
284, 287, 288 utilization, 84
retrieved countries, 287 useWindowSize(), 288
useFetch, 286 App component, 290
useFormatDate() creation, 289
formats, 75, 77 MyCounter component, 290
MyCounter uses, 291
component, 79, 80 utility, 280, 309
useFormatDate.js file, 78, 79 vs. directives, 264
useGeolocation(), 291, 292 createApp(App) method, 210, 211
MyCounter component, 293 Creation of custom directives
onMounted() method, 292 in directives file, 213, 214
uses, 294 lifecycle, DOM element
variables, 292 beforeMount(el,
useGeolocationWithDetails() binding), 208
API, 294, 295 beforeUnmount(el,
array, 296 binding), 209
if statement, 298 beforeUpdate(el,
latitude/longitude, 296, 297 binding), 208
MyCounter component, 296 binding.arg, 209
URL, 294, 295 binding.dir, 209
uses, 298 binding.instance, 209
watchEffect() method, 298 binding.modifiers, 209
useMap() composable (see binding.value, 209
useMap() composable) mounted() method, 209
349
INDEX
350
INDEX
D E
defineEmits() elem.name.common, 183
method, 166 Event-handling function, 151,
defineProps() 153, 154
method, 88, 90, 94 Event object
defineProps([“count”, in event-handling functions
“doubleCount”]) clear field content on click,
method, 103 158, 159
defineProps([“index”]) filter pressed key, 154–156
method, 121 handle content of
DevTools, 39 field, 155–157
console window, 40, 41 event.preventDefault() method, 155
extension, 40 Events
installation, Firefox, 39 App component, 150
Directives, 96, 264 communication
attributes communicate with child
end attribute, 86, 88–91 component, 161–163
init attribute, 85, 86, 88–91 communicate with parent
limits attribute, 91, 92, 94 component, 164–166
object as form “increment()”/”increment”,
attributes, 92–95 151, 152
HTML element, 95 increment() method, 149, 150
v-bind (see v-bind directive) inject() method, 167–169, 171
v-for (see v-for directive) MyCounter component, 150
v-if and v-else (see v-if and provide() method, 167–169, 171
v-else directives) v-on directive, 149
v-model (see v-model directive) event.target.value, 157
v-show directive, 113
directives.js file, 214
Document Object Model (DOM), F
4–8, 23, 51, 55, 148, 182, fetch(url) method, 183, 281
202, 203, 208–211, 219, 277, filter() method, 187, 197, 333, 334
279, 301, 304 focus() method, 202, 203
351
INDEX
I L
Lazy modifier, 146
increment() function, 149
limits array, 126, 127
Index attribute, 120, 121, 126
init variable, 87
inject() method, 167–169, 171, 199,
200, 202
M
main.js file, 22, 23
map() method, 332, 333
J Modifiers, 146
JavaScript lazy, 146, 147
arrays (see Arrays) trim, 146
arrow functions (see Arrow Modules
functions) definition, 321
async statement, 338–340 export statement, 324, 326, 328
asynchronous functions (see export default
Asynchronous functions) syntax, 325–328
await statement, 338–340 HTML files, 322
const keyword, 313–315 import
filter() method, 333, 334 statement, 323, 324
let keyword, 312, 313 keywords, 321
map() method, 332, 333 myModule.js file, 321
modules (see Modules) test.js file, 322
352
INDEX
353
INDEX
T
then() method, 184, 336, 338 V
Traditional functions, 328 v-bind directive, 96, 207
Trim modifier, 146 App component, 97, 101, 102
<input v-bind:value=”count” />
updates, 129
U MyCounter component,
Upper modifier, 216, 223, 224, 226 96, 98, 101
useMap() composable, 301 reactive variable, 98–100
MyCounter component, 302 use of {{count}}, 98
onMounted() method, 304 using attributes
useGeolocation in <script setup> section,
WithDetails(), 304 MyCounter
uses, 305 component, 103–105
354
INDEX
355
INDEX
356