- Conteúdo teórico e prático: 1h30
- Exercícios práticos: 1h30
Ao final desta aula, o aluno será capaz de:
- Compreender a sintaxe de templates Vue.js
- Usar interpolações de dados
- Dominar as principais diretivas Vue
- Trabalhar com eventos e manipulação de dados
- Aplicar estilos e classes dinamicamente
Templates Vue permitem criar interfaces dinâmicas e reativas. Utilizam sintaxe HTML estendida com:
- Interpolações: Inserir dados no HTML
- Diretivas: Instruções que modificam elementos
- Expressões: JavaScript diretamente no template
- Data Binding: Sincronização automática entre dados e view
A forma mais básica de inserir dados é usar {{ variavel }}:
<script setup>
import { ref } from 'vue'
const titulo = ref('Bem-vindo ao Vue')
const descricao = ref('Templates com Vue são muito poderosos!')
</script>
<template>
<div>
<h1>{{ titulo }}</h1>
<p>{{ descricao }}</p>
<p>A data de hoje é {{ new Date().toLocaleDateString('pt-BR') }}</p>
</div>
</template>Dentro das chaves é possível executar expressões JavaScript:
<script setup>
import { ref } from 'vue'
const ativo = ref(true)
const nome = ref('joão')
const numeros = ref([1, 3, 5, 7, 9])
</script>
<template>
<div>
<!-- Operações matemáticas -->
<p>2 + 2 = {{ 2 + 2 }}</p>
<!-- Operadores lógicos -->
<p>{{ ativo ? 'Ativo' : 'Inativo' }}</p>
<!-- Métodos string -->
<p>{{ nome.toUpperCase() }}</p>
<!-- Operações com arrays -->
<p>{{ numeros.filter(n => n > 5) }}</p>
</div>
</template>Para renderizar HTML como código (não apenas texto):
<script setup>
import { ref } from 'vue'
const html = ref('<strong>Isso é negrito</strong>')
</script>
<template>
<div>
<!-- Interpolação normal (segura, renderiza texto) -->
<p>{{ html }}</p>
<!-- v-html (renderiza como HTML) -->
<p v-html="html"></p>
</div>
</template>Renderização condicional:
<script setup>
import { ref } from 'vue'
const logado = ref(false)
const idade = ref(25)
const mostrarDetalhes = ref(true)
</script>
<template>
<div>
<!-- Mostrar se for true -->
<p v-if="logado">Bem-vindo de volta!</p>
<p v-else>Por favor, faça login</p>
<!-- Com múltiplas condições -->
<div v-if="idade < 18">
<p>Você é menor de idade</p>
</div>
<div v-else-if="idade >= 18 && idade < 65">
<p>Você está na idade de trabalhar, meu filho(a)</p>
</div>
<div v-else>
<p>Você é aposentado</p>
</div>
<!-- Template condicional (sem elemento wrapper) -->
<template v-if="mostrarDetalhes">
<h3>Detalhes</h3>
<p>Informações adicionais aqui</p>
</template>
</div>
</template>Similar a v-if, mas usa CSS display: none:
<script setup>
import { ref } from 'vue'
const visivel = ref(true)
</script>
<template>
<div>
<!-- v-show continua no DOM, apenas oculto -->
<p v-show="visivel">Este texto pode estar oculto</p>
<button @click="visivel = !visivel">
Alternar visibilidade
</button>
</div>
</template>v-if vs v-show:
v-if: Elemento removido do DOM (melhor para itens que não alternam frequentemente)v-show: Apenas display:none (melhor para alternâncias frequentes)
Renderizar listas:
<script setup>
import { ref } from 'vue'
const lista = ref(['Maçã', 'Banana', 'Laranja'])
const pessoas = ref([
{ id: 1, nome: 'Ana', idade: 28 },
{ id: 2, nome: 'Bruno', idade: 32 },
{ id: 3, nome: 'Carlos', idade: 25 }
])
const itens = ref(['Item 1', 'Item 2', 'Item 3'])
const config = ref({
tema: 'escuro',
idioma: 'pt-BR',
notificacoes: true
})
</script>
<template>
<div>
<!-- Array de strings -->
<ul>
<li v-for="frutas in lista" :key="frutas">
{{ frutas }}
</li>
</ul>
<!-- Array de objetos -->
<div v-for="pessoa in pessoas" :key="pessoa.id" class="card">
<h3>{{ pessoa.nome }}</h3>
<p>Idade: {{ pessoa.idade }}</p>
</div>
<!-- Com índice -->
<div v-for="(item, index) in itens" :key="index">
{{ index + 1 }}. {{ item }}
</div>
<!-- Iterando objetos -->
<div v-for="(valor, chave) in config" :key="chave">
{{ chave }}: {{ valor }}
</div>
</div>
</template>Importância da chave :key:
- Ajuda Vue a rastrear qual item mudou
- Importante para componentes internos (mantém estado)
- Use um id único, não índice quando possível
Ouvir eventos:
<script setup>
import { ref } from 'vue'
const contador = ref(0)
const saudar = (nome) => {
alert(`Olá, ${nome}!`)
}
const processar = (evento) => {
console.log(evento)
}
const enviarFormulario = () => {
console.log('Formulário enviado')
}
const buscar = () => {
console.log('Buscando...')
}
const ativo = ref(false)
</script>
<template>
<div>
<!-- Clique em botão -->
<button @click="contador++">
Cliques: {{ contador }}
</button>
<!-- Com método -->
<button @click="saudar('João')">
Saudar
</button>
<!-- Evento com parâmetro e objeto evento -->
<button @click="processar($event)">
Processar
</button>
<!-- Modificadores de evento -->
<form @submit.prevent="enviarFormulario">
<!-- .prevent previne comportamento padrão -->
<input type="text" />
<button type="submit">Enviar</button>
</form>
<!-- Eventos de teclado -->
<input @keyup.enter="buscar" placeholder="Digite e pressione Enter" />
<!-- Eventos de mouse -->
<div @mouseover="ativo = true" @mouseleave="ativo = false">
{{ ativo ? 'Mouse aqui!' : 'Passe o mouse' }}
</div>
</div>
</template>Modificadores de Evento Comuns:
.prevent-preventDefault().stop-stopPropagation().self- Apenas se o evento vem do próprio elemento.once- Dispara apenas uma vez.capture- Usa modo capture.enter,.space,.esc- Modificadores de teclado
Vincular atributos e propriedades:
<script setup>
import { ref, computed } from 'vue'
const imagemUrl = ref('https://via.placeholder.com/150')
const descricaoImagem = ref('Imagem de exemplo')
const estaAtivo = ref(true)
const temErro = ref(false)
const classe1 = ref('classe-um')
const classe2 = ref('classe-dois')
const corTexto = ref('blue')
const tamanho = ref(18)
const estilosComputados = computed(() => ({
backgroundColor: estaAtivo.value ? 'green' : 'gray',
padding: '10px',
borderRadius: '5px'
}))
const botaoDesabilitado = ref(false)
</script>
<template>
<div>
<!-- Atributo simples -->
<img :src="imagemUrl" :alt="descricaoImagem" />
<!-- Classe dinâmica -->
<div :class="{ ativo: estaAtivo, erro: temErro }">
Elemento com classes dinâmicas
</div>
<!-- Array de classes -->
<div :class="[classe1, classe2]">
Classes de array
</div>
<!-- Estilo inline dinâmico -->
<div :style="{ color: corTexto, fontSize: tamanho + 'px' }">
Texto com estilo dinâmico
</div>
<!-- Objeto de estilos -->
<div :style="estilosComputados">
Estilo com objeto
</div>
<!-- Atributo booleano -->
<button :disabled="botaoDesabilitado">
Clique aqui
</button>
</div>
</template>
<style scoped>
.ativo {
background-color: #42b883;
color: white;
}
.erro {
border: 2px solid red;
}
.classe-um {
font-weight: bold;
}
.classe-dois {
text-transform: uppercase;
}
img {
max-width: 30%;
height: auto;
}
</style>Two-way data binding (sincronização bidirecional):
<script setup>
import { ref } from 'vue'
const nome = ref('')
const mensagem = ref('')
const aceitar = ref(false)
const opcao = ref('opcao1')
const pais = ref('')
const interesses = ref([])
</script>
<template>
<div>
<!-- Input de texto -->
<label>Nome:</label>
<input v-model="nome" />
<p>Você digitou: {{ nome }}</p>
<!-- Textarea -->
<label>Mensagem:</label>
<textarea v-model="mensagem"></textarea>
<!-- Checkbox -->
<label>
<input type="checkbox" v-model="aceitar" />
Aceito os termos
</label>
<p>{{ aceitar ? 'Aceito!' : 'Não aceito' }}</p>
<!-- Radio -->
<label>
<input type="radio" v-model="opcao" value="opcao1" />
Opção 1
</label>
<label>
<input type="radio" v-model="opcao" value="opcao2" />
Opção 2
</label>
<!-- Select -->
<select v-model="pais">
<option value="">Selecione um país</option>
<option value="br">Brasil</option>
<option value="pt">Portugal</option>
<option value="us">Estados Unidos</option>
</select>
<!-- Multiple select -->
<select v-model="interesses" multiple>
<option value="vue">Vue</option>
<option value="react">React</option>
<option value="angular">Angular</option>
</select>
</div>
</template><script setup>
import { ref, computed } from 'vue'
const email = ref('')
const senha = ref('')
const enviando = ref(false)
const emailValido = computed(() => {
return email.value.includes('@') && email.value.includes('.')
})
const senhaFraca = computed(() => {
return senha.value.length < 6
})
const formularioValido = computed(() => {
return emailValido.value && !senhaFraca.value
})
const enviar = async () => {
enviando.value = true
// Simular envio
await new Promise(r => setTimeout(r, 2000))
alert('Formulário enviado com sucesso!')
enviando.value = false
email.value = ''
senha.value = ''
}
</script>
<template>
<div class="formulario">
<h2>Formulário de Cadastro</h2>
<div>
<label>Email:</label>
<input v-model="email" type="email" />
<p v-if="!emailValido" class="erro">Email inválido</p>
</div>
<div>
<label>Senha:</label>
<input v-model="senha" type="password" />
<p v-if="senhaFraca" class="aviso">Senha muito fraca</p>
</div>
<button @click="enviar" :disabled="!formularioValido">
{{ enviando ? 'Enviando...' : 'Enviar' }}
</button>
</div>
</template>
<style scoped>
.formulario {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
div {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
width: 100%;
padding: 10px;
background-color: #42b883;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.erro {
color: red;
font-size: 0.9rem;
}
.aviso {
color: orange;
font-size: 0.9rem;
}
</style><script setup>
import { ref, computed } from 'vue'
const novoItem = ref('')
const itens = ref([])
const adicionar = () => {
if (novoItem.value.trim()) {
itens.value.push({
texto: novoItem.value,
concluido: false
})
novoItem.value = ''
}
}
const remover = (index) => {
itens.value.splice(index, 1)
}
const itensConcluidos = computed(() => {
return itens.value.filter(item => item.concluido).length
})
</script>
<template>
<div class="lista-app">
<h2>Minhas Tarefas</h2>
<div class="adicionar">
<input
v-model="novoItem"
@keyup.enter="adicionar"
placeholder="Adicione uma nova tarefa"
/>
<button @click="adicionar">Adicionar</button>
</div>
<div v-if="itens.length === 0" class="vazio">
Nenhuma tarefa ainda!
</div>
<ul v-else>
<li
v-for="(item, index) in itens"
:key="index"
:class="{ completo: item.concluido }"
>
<input
type="checkbox"
v-model="item.concluido"
/>
<span>{{ item.texto }}</span>
<button @click="remover(index)" class="btn-remover">
✕
</button>
</li>
</ul>
<p class="stats">
{{ itensConcluidos }} de {{ itens.length }} tarefas completas
</p>
</div>
</template>
<style scoped>
.lista-app {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.adicionar {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 10px 20px;
background-color: #42b883;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.vazio {
text-align: center;
color: #999;
padding: 20px;
}
ul {
list-style: none;
padding: 0;
}
li {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
border-bottom: 1px solid #eee;
}
li.completo span {
text-decoration: line-through;
color: #999;
}
.btn-remover {
margin-left: auto;
padding: 5px 10px;
background-color: #ff6b6b;
font-size: 0.9rem;
}
.stats {
text-align: center;
color: #666;
margin-top: 20px;
}
</style>Criar um componente que:
- Exiba uma cor em hexadecimal
- Tenha um botão "Gerar Cor Aleatória"
- Mude a cor de fundo toda vez que clicar
- Exiba o nome da cor (se possível) ou código RGB
Criar um componente que:
- Tenha um input que receba texto
- Mostre em tempo real o texto digitado normalmente
- Mostre o texto invertido
- Mostre estatísticas (caracteres, palavras)
- Tenha um botão para copiar para clipboard
Criar um componente com:
- Uma lista de produtos (nome, preço, categoria)
- Input de busca por nome
- Seleção de categoria (todas, eletrônicos, roupas, etc)
- Exibir produtos filtrados
- Mostrar quantidade de produtos encontrados
Criar um componente com:
- Múltiplas abas (Sobre, Contato, Serviços)
- Conteúdo diferente para cada aba
- Indicador visual da aba ativa
- Transição suave entre abas
Criar um calculadora que:
- Tenha dois inputs (números)
- Operações: +, -, *, /
- Exiba o resultado
- Trate divisão por zero
- Limite a duas casas decimais
- Entendo interpolação básica com
{{ }} - Consigo usar expressões JavaScript em templates
- Domino
v-if,v-else-if,v-else - Consigo renderizar listas com
v-for - Entendo a importância da
:keyem listas - Consigo ouvir eventos com
@ - Domino binding de atributos com
: - Consigo usar
v-modelem formulários - Consigo trabalhar com classes e estilos dinâmicos
- Completei todos os 5 exercícios propostos