vueschool.io @vueschool_io
Teacher @ Vue School
Full Stack developer (10 years)
Husband and Father
Creator of Vue.js
npmtrends.com/vue
https://www.npmjs.com/package/vue
Static HTML files
<!-- hello.html -->
<html>
<body>
<script src="https://unpkg.com/vue@3"></script>
</body>
</html>
Content Delivery Network
<!-- hello.html -->
<html>
<body>
<script src="https://unpkg.com/vue@3"></script>
<script>
// we can use Vue in here
</script>
</body>
</html>
<script>
Vue.createApp({})
</script>
<script src="https://unpkg.com/vue@next"></script>
<div id="app"></div>
<script>
Vue.createApp({}).mount('#app')
</script>
⚡️ Vue Super Powers
<div id="app"></div>
<script>
Vue.createApp({}).mount('#app')
</script>
<div id="app">
{{message}}
</div>
<script>
Vue.createApp({
data() {
return {
message: 'Hello Vueschool'
}
}
}).mount('#app')
</script>
Options API
<script>
Vue.createApp({
data() {
return {
message: 'Hello Vueschool'
}
}
}).mount('#app')
</script>
Must be a function
Must return a data object
<div id="app">
{{message}}
<input v-model="message">
</div>
<script>
Vue.createApp({
data() {
return {
message: 'Hello Vueschool'
}
}
}).mount('#app')
</script>
v-model directive
Let's try it together!
in every place you want to display the message
⏰ 15 mins
<div id="app">
{{message}}
<input v-model="message">
</div>
<div id="app">
<img v-bind:src="image.path" v-bind:alt="image.alt" />
</div>
<script>
Vue.createApp({
data() {
return {
image: {
path: '/path/to/image.jpg',
alt: 'Alternative text for an image'
}
}
}
}).mount('#app')
</script>
<div id="app">
<img src="/path/to/image.jpg" alt="Alternative text for an image" />
</div>
Rendered Output
<div id="app">
<a v-bind:href="link.href">{{ link.text }}</a>
</div>
<script>
Vue.createApp({
data() {
return {
link: {
href: 'https://amazon.com',
text: 'Amazon'
}
}
}
}).mount('#app')
</script>
<div id="app">
<a href="https://amazon.com">Amazon</a>
</div>
Rendered Output
<div id="app">
<img v-bind:src="image.path" v-bind:alt="image.alt" />
<img :src="image.path" :alt="image.alt" />
</div>
<div id="app">
<p v-if="sunny">
It's sunny today! 🌞
</p>
</div>
<script>
Vue.createApp({
data() {
return {
sunny: true
}
}
}).mount('#app')
</script>
<div id="app">
<p v-if="sunny">It's sunny today! 🌞</p>
<p v-else>Is it raining? ☔️</p>
</div>
<script>
Vue.createApp({
data() {
return {
sunny: true
}
}
}).mount('#app')
</script>
<div id="app">
<p v-if="sunny">It's sunny today! 🌞</p>
<p v-else-if="windy">It's windy today! 🌬</p>
<p v-else>Is it raining? ☔️</p>
</div>
<script>
Vue.createApp({
data() {
return {
sunny: false,
windy: true
}
}
}).mount('#app')
</script>
<div id="app">
<p v-if="weather === 'sunny'">It's sunny today! 🌞</p>
<p v-else-if="weather === 'windy'">It's windy today! 🌬</p>
<p v-else>Is it raining? ☔️</p>
</div>
<script>
Vue.createApp({
data() {
return {
weather: 'hurricane'
}
}
}).mount('#app')
</script>
Keep it simple or extract the logic
⏰ 15 mins
<script>
Vue.createApp({
data() {
return {
users: [
{id: 1, name: 'Bruce Wayne', alterEgo: 'Batman'},
{id: 2, name: 'Clark Kent', alterEgo: 'Superman'},
{id: 3, name: 'Tony Stark', alterEgo: 'Ironman'},
{id: 4, name: 'Diana Prince', alterEgo: 'Wonder Woman'}
]
}
}
}).mount('#app')
</script>
<div v-for="something in someArray">
<!-- something is available here -->
</div>
<div id="app">
<ul>
<li v-for="food in favoriteFood">{{ food }}</li>
</ul>
</div>
<script>
Vue.createApp({
data() {
return {
favoriteFood: [
'Pizza',
'Burger',
'Hummus'
]
}
}
}).mount('#app')
</script>
<div>
<ul>
<li>Pizza</li>
<li>Burger</li>
<li>Hummus</li>
</ul>
</div>
Rendered Output
<div v-for="user in users">
{{ user.name }}
</div>
The item is available inside the element
The item is available inside child elements
<div v-for="user in users">
{{ user.name}}
<span class="pull-right">{{ user.alterEgo }}</span>
</div>
<img
v-for="image in myImages"
:src="image.src"
:alt="image.title" />
The item is available on the element
<div id="app">
<table>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
</tr>
</table>
</div>
<script>
Vue.createApp({
data() {
return {
users: [
{ id: 1, name: 'Huey' },
{ id: 2, name: 'Dewey' },
{ id: 3, name: 'Louie'},
]
}
}
}).mount('#app')
</script>
⏰ 20 mins
👋
See you tomorrow
👋
Welcome Back!
<button v-on:click="raining = true">Make it rain!</button>
<button v-on:click="raining = true">Make it rain!</button>
<button v-on:click="makeItRain">Make it rain!</button>
<div id="app">
<button v-on:click="makeItRain">Make it rain!</button>
</div>
<script>
Vue.createApp({
data() {
return {
raining: false
}
},
methods: {
makeItRain () {
this.raining = true
}
}
}).mount('#app')
</script>
Methods are defined on the Vue instance
<script>
Vue.createApp({
data() {
return {
raining: false
}
},
methods: {
makeItRain () {
this.raining = true
},
makeItRainAndSayHi () {
this.makeItRain()
alert('How you doing?')
}
}
}).mount('#app')
</script>
We can access the entire Vue application instance under this
<button @click="makeItRain">Make it rain!</button>
<button v-on:click="makeItRain">Make it rain!</button>
Instead of
<form @submit.prevent="submitForm">
...
</form>
submitForm(event) {
event.preventDefault()
// ...
}
Let's try it together!
Source vuejs.org
<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="doThis"></a>
<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- modifiers can be chained -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- just the modifier -->
<form v-on:submit.prevent></form>
<!-- only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div v-on:click.self="doThat">...</div>
<textarea @keydown.esc="clearText" />
.enter
.tab
.delete
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
.a
.b
....
Let's try it together!
⏰ 25 mins
Won't Do these slides in the vertical
⚠️
🔥
Let's do a review quiz
<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
<ul v-if="showDropdown" class="dropdown-menu" >
<!-- dropdown items goes here -->
</ul>
<button v-else @click="openDropdown">Menu</button>
</div>
<div v-click-outside="closeDropdown">
// ...
</div>
A custom directive
<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
<ul v-if="showDropdown" class="dropdown-menu" >
<!-- dropdown items goes here -->
</ul>
<button v-else @click="openDropdown">Menu</button>
</div>
Directive
Argument
Value
<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
<ul v-if="showDropdown" class="dropdown-menu" >
<!-- dropdown items goes here -->
</ul>
<button v-else @click="openDropdown">Menu</button>
</div>
Directive
(no argument or value)
<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
<ul v-if="showDropdown" class="dropdown-menu" >
<!-- dropdown items goes here -->
</ul>
<button v-else @click="openDropdown">Menu</button>
</div>
Directive
(no argument)
Value
Let's see how it works with the last exercise
Install it now 🙃
<div id="app">
{{ message.split('').reverse().join('') }}
</div>
Let's try it together!
<div id="app">
<h1>{{ message.split('').reverse().join('') }}</h1>
<input v-model="message">
<div>{{ message.split('').reverse().join('') }}</div>
<span>{{ message.split('').reverse().join('') }} </span>
</div>
<script>
Vue.createApp({
data() {
return {
message: 'To infinity and beyond'
}
},
computed: {
reversedMessage () {
return this.message.split('').reverse().join('')
}
}
}).mount('#app')
</script>
<div id="app">
{{ reversedMessage }}
</div>
Let's try it together!
They are used to:
<div id="app">
<h1>{{ reversedMessage }}</h1>
<input v-model="message">
<div>{{ reversedMessage }}</div>
<span>{{ reversedMessage }} </span>
</div>
<script>
Vue.createApp({
data() {
return {
message: 'To infinity and beyond'
}
},
computed: {
reversedMessage () {
return this.message.split('').reverse().join('')
}
}
}).mount('#app')
</script>
<div id="app">
<p v-if="isSunny">It's sunny today!</p>
</div>
<script>
Vue.createApp({
data() {
return {
weather: 'sunny'
}
},
computed: {
isSunny () {
return this.weather === 'sunny'
}
}
}).mount('#app')
</script>
<div id="app">
<p v-if="isSunny">It's sunny today!</p>
</div>
<script>
Vue.createApp({
data() {
return {
weather: 'sunny'
}
},
computed: {
isSunny () {
return this.weather === 'sunny'
}
}
}).mount('#app')
</script>
<div id="app">
<p v-for="item in itemsInStock" :key="item.id">{{ item.name }}</p>
</div>
<script>
Vue.createApp({
data() {
return {
items: [
{id: 1, name: 'Hat', quantity: 7, price: 498},
{id: 2, name: 'Sock', quantity: 0, price: 59},
{id: 3, name: 'Shoes', quantity: 4, price: 1189}
]
}
},
computed: {
itemsInStock() {
return this.items.filter(item => item.quantity > 0)
}
}
}).mount('#app')
</script>
<div id="app">
<p>Cart total: {{ shoppingCartTotal }}</p>
</div>
<script>
Vue.createApp({
data() {
return {
cart: [
{name: 'Hat', quantity: 1, price: 498},
{name: 'Sock', quantity: 3, price: 59},
{name: 'Shoes', quantity: 1, price: 1189}
]
}
},
computed: {
shoppingCartTotal() {
return this.cart.map(item => item.price * item.quantity)
.reduce((total, amount) => total + amount)
}
}
}).mount('#app')
</script>
<div id="app">
<p>Cart total: {{ shoppingCartTotal }}</p>
</div>
<script>
Vue.createApp({
data() {
return {
cart: [
{name: 'Hat', quantity: 1, price: 498},
{name: 'Sock', quantity: 3, price: 59},
{name: 'Shoes', quantity: 1, price: 1189}
]
}
},
computed: {
shoppingCartTotal() {
return this.cart.map(item => item.price * item.quantity)
.reduce((total, amount) => total + amount)
}
}
}).mount('#app')
</script>
<div id="app">
<h1>Total is {{amountInDollars}}</h1>
</div>
<script>
Vue.createApp({
data() {
return {
amount: 344
}
},
computed: {
amountInDollars () {
return `$${this.amount}.00`
}
}
}).mount('#app')
</script>
<div id="app">
<h1>Total is {{amountInDollars}}</h1>
</div>
<script>
Vue.createApp({
data() {
return {
amount: 344
}
},
computed: {
amountInDollars () {
return `$${this.amount}.00`
}
}
}).mount('#app')
</script>
computed: {
itemsInStock() {
return this.items.filter(
item => item.quantity > 0
)
}
}
methods: {
setWeather (weather) {
this.weather = weather
}
}
⏰ 25 mins