Criando um componente de tabela - Angular 2+
Com certeza um dos recursos mais utilizados para os desenvolvedores web é boa e velha tabela de dados. Quando comecei no mundo Angular tive um pouco de dificuldade quando me deparei com a construção de um componente de tabela, então vou compartilhar uma das formas que aprendi para criar este componente. Com certeza alguns recursos que serão abordados neste texto poderão ser utilizados em outros lugares do seu projeto.
Iniciando a construção do componente tabela
Inicialmente vamos estruturar um componente com a premissa de que os dados passados para a tabela terão a seguinte estrutura: { id: number, nome: string }
O código ainda está bem simples mas já é um ponto de partida. Para testarmos o componente no AppComponent
, por exemplo, vamos importar o módulo TabelaModule
dentro do AppModule
conforme abaixo:
Uma vez importado o TabelaModule
no AppModule
podemos utilizar o app-tabela
dentro do AppComponent
:
Aqui vale uma observação, este site (https://www.json-generator.com/) ajuda a gerar um JSON com dados aleatórios para efetuar seus testes no formato que você configurar.
Agora que o código está funcionando vamos focar no problema das colunas. Até este momento seguimos a premissa de que a estrutura dos dados recebidos na tabela terão apenas duas informações:
id
nome
Como a ideia é que este componente possa ser utilizado para qualquer matriz de dados, vamos evoluir um pouco mais essa questão.
Parametrizando as colunas
Podemos imaginar algumas formas de deixar a renderização das colunas de forma dinâmica. Uma das possíveis soluções é supor que cada item dentro do array tenha exatamente a mesma estrutura, então sempre que o app-tabela
receber um novo dado dentro do @Input() dados
podemos pegar o primeiro item e verificar quais são suas propriedades através do Object.getOwnPropertyNames(obj)
, veja no código abaixo:
E agora adicionamos um loop for no <td>
:
Observação: podemos acessar a propriedades de um objeto através dos colchetes, como fizemos no tabela.component.html
logo acima.
console.log(objeto.nome);
// ou
console.log(objeto['nome']);
O resultado final é exatamente como o anterior, porém agora as colunas são renderizadas dinâmicamente:
Nesta solução consideramos como premissa que cada item dentro do array dados
tem exatamente a mesma estrutura. Caso esta condição não seja satisfeita, conforme abaixo, o resultado não será como nós esperamos e a coluna age
(exemplo abaixo) não será renderizada pois não consta no primeiro item do array.
this.dados = [{
"id": 442,
"nome": "Myrtle"
}, {
"id": 376,
"nome": "Georgette",
"age": 30,
}, {
"id": 882,
"nome": "Manning",
"age": 14,
}];
Antes de pensarmos em uma solução para o problema acima, vale destacar um outro ponto que ainda não foi comentado, eventualmente o desenvolvedor que irá utilizar seu app-tabela
precisará renderizar uma coluna como um link, uma imagem, alterar a cor da célula dependendo de um valor, etc. Nesta estrutura não conseguimos chegar no resultado abaixo por exemplo:
<table>
<tr>
<td>442</td>
<td>Myrtle</td>
<!-- Deixar uma coluna como um link: -->
<td><a href="/teste?id=442">editar</a></td>
</tr>
</table>
Permitindo que o usuário customize as colunas
Para que possamos permitir uma customização dentro do nosso <td>
que está dentro do app-tabela
, podemos utilizar um ng-container
e "recheá-lo" com o que o usuário passar.
A primeira coisa é que o desenvolvedor que está utilizando o app-tabela
deverá indicar como as informações serão renderizadas para cada coluna. Para isto ele deve passar um <ng-template>
para cada coluna conforme abaixo:
Já no nosso componente, app-tabela
, precisamos pegar as referências destes <ng-template>
. Para pegar o que é passado dentro do componente, <app-tabela>aqui é dentro do componente!</app-tabela>
, podemos utilizar o @ContentChildren
conforme o código abaixo:
Por fim, no template precisamos fazer um loop em cima do colunas
e passar como referência o item (dadosLinha
) do array dados
:
Basicamente dentro do componente nós construímos nossa estrutura básica e deixamos um <ng-container>
onde o usuário do componente pode customizar. Fazendo uma analogia, funciona como um carro com motor flex, a estrutura do carro está pronta para uso (componente) mas quem decide qual combustível (etanol ou gasolina) utilizar é você (desenvolvedor).
Veja que agora o usuário passa uma lista de <ng-template>
que o componente app-tabela
utiliza para renderizar o conteúdo dentro do <td>...</td>
. Com isto o componente torna-se mais flexível para atender outras demandas.
Perceba que este recurso pode ser utilizado em vários lugares para deixar os componentes mais flexíveis. Eu mesmo já utilizei este recurso em um Popover, onde eu criei um formato padrão de exibição do popover mas permitindo que o desenvolvedor possa customizá-lo através de um <ng-template>
.
Tratando o cabeçalho
Até este momento não comentamos sobre o cabeçalho da tabela, mas podemos seguir a mesma lógica criada logo acima:
Considerações
Veja que para construir este componente acabamos passando pelo @ContentChildren
, <ng-template>
e <ng-container>
. Como já dito no início deste texto, com certeza estes recursos poderão ser aproveitados em outros lugares do seu projeto. Abaixo vou deixar um link para o código fonte completo no Stackblitz e alguns links legais para uma posterior leitura.
Links interessantes: