Criando componentes reutilizáveis (mestre/detalhe)

Quando trabalhamos com componentes, seja em qualquer framework (com vue fica mais divertido!), estamos utilizando um dos requisitos mais básicos da programação que é dividir um problema em diversos problemas menores. Neste caso, dividimos uma interface em pedaços, e a montamos como se fosse um lego. O uso de componentes é vital para um perfeito entendimento de um sistema, pois seria humanamente impossível criar todo um sistema em apenas um arquivo.

Recomendo a leitura do artigo Pensando em web componentes para você compreender melhor a filosofia do conceito

Dentre as diversas utilizadas da criação de componentes, temos a reutilização do mesmo em diversas partes do sistema, podendo inclusive agrupar componentes em uma hierarquia mestre/detalhe, como no esboço a seguir.

Exemplo de composição

O que temos nesta imagem é a distinção entre dois tipos de componentes. O primeiro, que chamamos de “formulário pai” - em cinza - contém alguns elementos como o título do formulário e os botões Save e Clear. Dentro deste formulário, temos o “formulário filho” - em branco - que contém os campos do mesmo. Se imaginarmos um sistema relativamente complexo, teremos dezenas de formulários que possuem um design muito similar, com algum elementos da tela sendo os mesmos. A partir desta necessidade, podemos criar um componente padrão, que conteria as seguintes particularidades:

  • Uma barra de título
  • Uma caixa de informação contendo um texto qualquer (por exemplo, um help)
  • O lugar onde os controles de formulário seriam incluídos (chamamos de slot)
  • Um botão save
  • Um botão clear

Quando este componente estiver pronto, ele poderá ser usado para desenhar diversas telas que contém formulários, sendo que você necessitará apenas incluir os campos do formulário, sem se preocupar com demais funcionalidades.

Objetivo deste artigo

Criar um componente capaz de agrupar algumas funções similares dos formulários, como uma barra de títulos, o botão salvar, etc. O esboço a seguir ilustra como o componente será:

image

Preparando o projeto

Pode-se aplicar todos os conceitos que serão aprendidos aqui em um projeto existente. Mas neste exemplo de projetos iremos criar algo do zero, através do vue-cli. Primeiro, certifique-se de possuir o Node na versão 8 ou superior, e também o npx, para que não seja necessário instalar o vue-cli no seu sistema. Criaremos o projeto com o seguinte comando:

npx vue-cli init webpack myproject

Use as opções padrão do template, pois elas são importantes para um projeto de média escala. Apesar de estarmos apenas demonstrando uma funcionalidade, é interessante já conhecer tudo que o template webpack tem a oferecer (como ESLInt, Nightwatch etc).

Após a criação do projeto, abra ele no seu editor de textos predileto. Eu recomendo o Visual Studio Code. Nossa primeira tarefa é executar o comando npm run dev e verificar se o navegador abre com o projeto em execução, como na imagem a seguir:

image

Instalando um CSS Framework

Você pode instalar qualquer tipo de CSS Framework (Bulma, Material, Boostrap) ou UI Toolkit (iView, Vuetify). Neste artigo usaremos Bulma, mais precisamente o Buefy, um port do Bulma para o Vue. Para instalá-lo, digite:

npm i -S buefy

Após a instalação, ele estará disponível no node_modules. Preciamos apenas configurá-lo no Vue, alterando o arquivo src/main.js para:

Adicionamos 3 linhas ao arquivo. Primeiro, o import Buefy from 'buefy' para importar o Buefy que está no diretório node_modules. Logo abaixo incluímos o CSS do Buefy (necessário em qualquer framework css), e finalmente usamos Vue.use(Buefy) para importar o Buefy no Vue.

Alterando um pouco a estrutura do projeto

Quando usamos o template do webpack, temos a seguinte estrutura de projeto:

O arquivo principal do projeto é o main.js, o mesmo que incluímos o Buefy. O arquivo App.vue é o componente principal da aplicação, que possui o seguinte código:

A mudança que iremos realizar aqui é:

  • Remover o logotipo
  • Remover o <style>
  • Trocar <div> para <section>

No final, teremos:

O arquivo App.vue possui agora apenas o router-view, que é onde o Vue Router irá carregar os componentes de acordo com a rota estabelecida. Trocamos também o <div> por <section> para se adequar ao padrão do Buefy.

Neste artigo iremos utilizar a rota padrão do HelloWorld.vue, que pode ser observada no arquivo src/routes/index.js.

Para finalizar, abra o arquivo HelloWorld.vue e altere para:

Removemos o código html que exibia algumas informações sobre o Vue, trocamos o <div> por <section> e removemos o <style>. No final, temos a app com a seguinte interface:

image

Criando o componente FormBox

O componente FormBox já possui o seguinte esboço:

image

Para criar o componente, crie o arquivo FormBox.vue em src/components, inicialmente com o seguinte código:

Vamos agora alterar o HelloWorld.vue incluindo o componente recém criado FormBox.vue. Veja:

Para incluir um componente em outro componente, é necessário realizar 3 procedimentos:

  1. Realizar o import do componente na seção <script>
  2. Adicionar o componente na propriedade components da instância Vue
  3. Adicionar o componente na seção <template> usando kebab case. Ou seja, FormBox se torna <form-box>

O resultado até o momento é algo semelhante a figura a seguir:

image

Acima, temos o texto do componente FormBox, e podemos também ver o Vue DevTools, uma extensão para o Google Chrome (tem para firefox também). No DevTools, pode-se ver a hierarquia de componentes.

Configurando o componente FormBox

Agora que já criamos o componente FormBox e já o incluímos no HelloWorld.vue, podemos codificá-lo de forma que fique semelhante ao que propomos.

Primeiro, criamos o código reaproveitando o exemplo encontrado no Buefy, veja:

Então temos que, a princípio, realizar a comunicação entre o componente pai e o componente filho. Por exemplo, o título do FormBox não pode ser fixo, quem o determina é o componente HelloWorld. Configuramos isso através de uma funcionalidade do Vue chamada props.

Configurando o título do FormBox

Para incluir uma propriedade no componente filho, usamos a propriedade props na instância do Vue, da seguinte forma:

Alteramos o código em duas partes. Primeiro, adicionamos :title='title' no componente bMessage do Buefy. Você já deve saber que o uso do : antes da propriedade é um atalho para v-bind. A outra alteração é na instância, incluindo a propriedade title através do props. Se estiver com dúvidas sobre o props, leia a documentação.

Voltando ao componente Hello World, podemos alterá-lo para:

 <form-box title="Hello Form Box"></form-box>

Perceba que agora passamos a propriedade title do pai para o filho, realizando a comunicação (pausa para aplausos)

Até agora temos o seguinte resultado (veja no Vue DevTools a propriedade title):

image

Configurando o campo de mensagem do FormBox

Assim como fizemos no title, vamos configurar a propriedade message que irá aparecer logo abaixo do título.

Primeiro, a criamos no FormBox.vue:

Além de adicionar a propriedade no props, a referenciamos da seguinte forma:

<b-tag type="is-warning" v-show="message"></b-tag>

Usamos o componente bTag do Buefy, junto com uma checagem se a propriedade foi definida através do v-show.

Voltando ao componente HelloWorld.vue, podemos adicionar a propriedade message da seguinte forma:

<form-box title="Hello Form Box" message="A help message"></form-box>

Com isso, teremos o seguinte resultado:

image

Adicionando os botões Save e Clear

Em nossa especificação, o componente FormBox possui dois botões: Save e Clear. Estes botões estarão no componente, mas quem deve cuidar da lógica no click de cada botão é o componente pai que está usando o FormBox. Assim, podemos ter vários FormBox na aplicação, mas o que acontece quando se clica no botão será responsabilidade do componente pai.

O Vue trbalha com eventos de forma bastante simples, através dos métodos $emit (disparar evento) e on (receber evento).

Primeiro, vamos adicionar o botão Save e o botão Clear ao FormBox:

Neste código, temos o uso do @click no botão Save (poderia ser v-on:click) e no próprio parâmetro da propriedade repassamos o $emit. Já no botão Clear, apenas para diferenciar (use de acordo com o que achar melhor), chamamos o método onClearClick que está definido na propriedade methods da instância Vue do componente. Neste método, usamos this.$emit('clear').

Perceba que a responsabilidade do que vai acontecer no clique do botão é do pai, não do componente. Então, para finalizar, vamos voltar ao HelloWorld.vue e adicionar tais funcionalidades.

Aqui temos a forma como capturar os eventos e chamar seus respectivos métodos. Perceba que temos v-on:save="onSave" e @clear="onClear", sendo que o segundo é o atalho do primeiro (@ é atalho para v-on). Os respectivos métodos apenas disparam um alert indicando o botão que foi clicado, veja:

image

Compondo o componente mestre/detalhe

Todo o nosso componente FormBox foi elaborado para ser reutilizável. Ou seja, se o objetivo é criar apenas uma casca do nosso formulário, deverá haver uma área onde será possível adicionar campos de caixa de texto, de seleção, etc (lembre-se do mockup lááá em cima).

O Vue trabalha com o conceito de slots, onde é possível embutir um conteúdo de um componente em outro.

Para compreender melhor, vamos alterar o componente FormBox.vue para:

O que fizemos foi adicionar <slot></slot> após o <b-tag> e antes do <hr/>. Com isso, estamos dizendo que, tudo que estiver entre <form-box> e </form-box> deverá ser embutido no slot.

Voltando ao HelloWorld.vue, podemos configurar o <form-box> da seguinte forma:

Aqui realizamos algumas modificações:

  1. Incluímos dois campos dentro da tag <form-box>
  2. Adicionamos as variáveis name e email na propriedade data da instância do componente Vue. Estas propriedades estão ligadas às duas caixas de texto através do v-model
  3. Alteramos o método onSave incluindo a informação dos campos digitados.

As testar a aplcação, temos a seguinte tela:

image

Agora uma outra pausa para os aplausos!! Criamos com o Vue um componente que pode ser reutilizado em diversas partes da sua aplicação, sendo que somente o “miolo” será controlado pelo componente pai.

Se você tiver alguma dúvida, fique a vontade em perguntar nos comentários abaixo.

Este projeto está no github: https://github.com/danielschmitz/vue-componente-composicao

Você pode acessar o demo do projeto aqui: http://danielschmitz.com.br/vue-componente-composicao/#/