Como criar um componente capaz de gerenciar abas
Neste artigo veremos como criar um componente no qual irá renderizar abas, que opdem conter os mais diversos conteúdos. O objetivo final é criar um componente que possui este comportamento:
Primeiro temos que estabelecer qual framework CSS usaremos. A partir dele podemos criar algumas regras para então poder criar o componente. Neste artigo usaremos o Bootstrap, que possui como base este código html:
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#home">Home</a></li>
<li><a data-toggle="tab" href="#menu1">Menu 1</a></li>
<li><a data-toggle="tab" href="#menu2">Menu 2</a></li>
</ul>
<div class="tab-content">
<div id="home" class="tab-pane fade in active">
<h3>HOME</h3>
<p>Some content.</p>
</div>
<div id="menu1" class="tab-pane fade">
<h3>Menu 1</h3>
<p>Some content in menu 1.</p>
</div>
<div id="menu2" class="tab-pane fade">
<h3>Menu 2</h3>
<p>Some content in menu 2.</p>
</div>
</div>
Este código html pode ser testando neste link.
Analisando a forma como o Bootstrap cria as abas, perceba que temos inicialmente a tag <ul>
e <li>
que representa as abas, e a tag <div class="tab-content">
que representa o conteúdo de cada aba. O próprio Bootstrap irá se encarregar de alterar o conteúdo de acordo com a aba qye será clicada.
O nosso objetivo então se “resume” a criar componentes em Vue que simulem a estrutura html acima.
Primeiro, crie o projeto usando vue-cli
:
vue init browserify-simple mytabs
cd mytabs
npm install
Após criar o projeto, vamos adicionar o jQuery+Botostrap da forma mais rápida e simples, pelo CDN, editando o arquivo index.html
, veja:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>vue-tabs</title>
<!-- bootstrap cdn-->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<app></app>
<!-- bootstrap cdn-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- App build -->
<script src="dist/build.js"></script>
</body>
</html>
Agora temos que descrever como seria o nosso componente! A princípio, algo como o código a seguir seria viável:
<tabs>
<tab name="t1" title="Tab 1" active=true>
Hello World
</tab>
<tab name="t2" title="Tab 2">
Hello World 2
</tab>
<tab name="t3" title="Tab 3">
Hello World 3
</tab>
</tabs>
Ou seja, temos um componente chamado Tabs
e outro chamado Tab
(No html é sempre em minúsculo). O componente Tab
é mais simpels, pois ele possui o texto de conteúdo e três propriedades. Vamos criá-lo primeiro? Então crie o diretório “components” e o arquivo “Tab.vue”:
<template>
<div role="tabpanel" class="tab-pane" :class="{'active': active}" id="">
<slot></slot>
</div>
</template>
<script>
export default{
props: {
title:{type:String,required: true},
active:{type:[Boolean,String],default:false},
name:{type:String,required: true}
}
}
</script>
Veja que, no template, criamos a div
que possui o mesmo conteúdo html que da aba do Bootstrap necessita, ou seja, os parâmetros role, class e id devem estar presentes. As propriedades do componente são o título, se a aba está ativa ou não, e o nome da aba, que é necessário para que o Bootstrap possa trocar o conteúdo de cada aba ao ser clicada.
O componente Tabs
é um pouco mais complexo, pois ele deve simular uma situação mais difícil. Primeiro, ele deve renderizar a lista de <ul>..<li>..</li>..</ul>
que formam as abas. Depois, eles devem renderizar cada div que é o conteúdo de cada aba.
Crie o componente components/Tabs.vue
, com o seguinte código:
<template>
<ul class="nav nav-tabs">
<li role="presentation" :class="{'active':tab.active}" v-for="tab in tablist" ><a href="#" aria-controls="" role="tab" data-toggle="tab"></a></li>
</ul>
<div class="tab-content">
<slot></slot>
</div>
</template>
<script>
export default{
data () {
return {
tablist: []
}
},
ready(){
//console.log(this.$children);
this.$children.forEach(c=>{
this.tablist.push({
"name": c.name,
"title": c.title,
"active": c.active
});
})
}
}
</script>
<style>
.nav-tabs {
background-color: #eeeeee;
padding-top:5px;
padding-left:5px;
border: 1px solid #ddd;
}
.tab-content {
color : #000000;
background-color: #ffffff;
padding : 5px 15px;
margin-top: -1px;
border: 1px solid #ddd;
}
</style>
No <template>
criamos a <ul>
que irá renderizar quantos <li>
forem necessários. A quantidade está ligada a um array chamado tablist
. Este array será criado no método ready
do componente. Criamos o ul...li
de acordo com o exemplo do Bootstrap, incluindo o elemento <a>
que irá apontar para uma aba específica.
Depois de criar o <ul>
, criamos <div>
que conterá o conteúdo de cada aba, e nesse momento usamos o <slot>
para que os componentes <tab>
sejam inseridos neste slot.
O maior detalhe deste componente é como deveremos preencher o array tablist
com as abas a serem criadas. Fazemos isso com o uso da propriedade this.$children
, nativa do Vue, que retorna um conjunto de elementos que estão presentes no <slot></slot>
do componente.
Utilizando this.$children
podemos saber quantos filhos o componente Tags
possui, e quais suas propriedades. Dessa forma, podemos montar a lista de <li>
preenchendo o array tablist
:
this.$children.forEach(c=>{
this.tablist.push({
"name": c.name,
"title": c.title,
"active": c.active
});
})
Na parte final do componente, temos o style, que dá uma graça no mesmo…
Com isso nós conseguimos fazer um componente de abas bem simples, onde é possível adicionar conteúdo em cada aba, de forma organizada, por exemplo:
<template>
<div id="app" class="container">
<h1> Tabs </h1>
<tabs>
<tab name="t1" title="Tab 1" active=true>
Hello World
</tab>
<tab name="t2" title="Tab 2">
Hello World 2
</tab>
<tab name="t3" title="Tab 3">
Hello World 3
</tab>
</tabs>
</div>
</template>
<script>
import Tabs from './components/Tabs.vue'
import Tab from './components/Tab.vue'
export default {
components: {
Tabs,Tab
}
}
</script>
Para adicionar mais elementos, ou elementos mais complexos, dêuma olhada no exemplo que está no github.