Normalmente utilizamos elementos <input>
para inserção de dados em formulários web. Já quando é preciso um controle maior utilizamos o <select>
, assim podemos limitar o tipo de informação que será enviada.
// exemplo input
<div class="field">
<label class="label" for="name">Produto</label>
<div class="control">
<input class="input" type="text" id="name">
</div>
</div>
// exemplo select
<div class="field">
<label class="label" for="bank">Bancos</label>
<div class="select" id="bank">
<select>
<option>Selecione</option>
<option>ABC</option>
</select>
</div>
</div>
A dificuldade começa quando precisamos criar um elemento <select>
filtrável. Muitas vezes temos esse tipo de situação quando queremos facilitar a seleção por uma quantidade muito grande de opções. Mesmo que as opções estejam ordenadas seria uma péssima experiência para o usuário ter que procurar entre mais de vinte, descendo pela barra de rolagem. Nessa situação o uso do <select>
deixa de fazer sentido, pois para tornar uma lista de opções filtrável precisamos que exista um campo de <input>
que permita digitação.
Podemos chegar no resultado esperado criando um <input>
e logo abaixo um elemento de conteúdo estático (<ul>
ou <div>
) listando as opções. A partir da inserção de caracteres no campo de <input>
podemos filtrar e mostrar apenas as opções que contenham parte do conteúdo digitado. Contudo também devemos nos preocupar em acrescentar funcionalidades inerentes ao <select>
:
- Quando o campo estiver em foco, mostrar opções
- Quando o campo não estiver em foco, esconder opções
- Quando clicar na opção desejada, preencher o campo de
<input>
// page.component.html
<div class="field">
<input class="input" type="text" formControlName="name"
[class.is-danger]="name.invalid && (name.dirty || name.touched)"
(blur)="isVisible(false)"
(focus)="isVisible(true)">
<div class="dropdown is-block"
[class.is-active]="suggestions.length > 0 && isVisible">
<div class="dropdown-menu" role="menu">
<div class="dropdown-content">
<ng-container *ngFor="let suggestion of suggestions" >
<a class="dropdown-item" (mousedown)="setValue(suggestion);">
{{suggestion.name}}
</a>
</ng-container>
</div>
</div>
</div>
</div>
Agora temos um componente de autocomplete com o mesmo comportamento de um <select>
, porém podemos observar a existência de uma sobrecarga de regras entre a lista e o <input>
. O desvio de responsabilidade torna o componente pouco flexível e de difícil manutenção, uma vez que parte substancial do código não define o motivo real da criação desse componente: ler e listar as sugestões.
A melhor forma de mantermos esse componente simples, flexível e sem perder a essência do <select>
seria criando a interação entre o <input>
e a lista de forma desacoplado por meio de um identificador entre eles. Isso já possível utilizando o elemento nativo <datalist>
com um id
de igual valor ao atributo list
em outro elemento de entrada.
// autocomplete.component.html
<datalist [id]="id">
<ng-container *ngFor="let suggestion of list">
<option [ngValue]="suggestion">{{suggestion.name}</option>
</ng-container>
</datalist>
// page.component.html
<div class="field">
<label class="label" for="bank">Bancos</label>
<div class="control">
<input class="input" type="text" id="bank"
formControlName="bank" list="bank-autocomplete"
[class.is-danger]="bank.invalid && (bank.dirty || bank.touched)">
</div>
<app-autocomplete [id]="'bank-autocomplete'" [list]="banks"></app-autocomplete>
</div>
Dessa forma construímos um componente com responsabilidade única que poderia ser estendido para receber uma lista dinâmica por requisição http, lista com sub-itens ou campo de entrada variado. O componente de autocomplete ficaria responsável por apresentar a lista e nada mais.
Top comments (2)
Ótimo assunto, e que bom que o Datalist já está homologado e implementado nos navegadores mais atuais: caniuse.com/#feat=datalist.
Acho ótima essa abordagem, só fico com um receio, hoje, dependendo do estilo que o Ux/Designer definir, temos uma grande dificuldade no frontend em modificar os estilos de componentes padrões do navegador, mas claro, vale uma conversa com o time de criação e experiência do usuário para entrar em acordo que envolve layout/experiência e performance.
Muito legal o artigo e o exemplo dado. Legal ver essa evolução dos componentes padrões dos navegadores atenderem mais os problemas que temos no dia-a-dia.
Como não é permitido customizar o estilo do
<datalist>
seria necessário troca-lo por outro elemento, se comprometendo com os efeitos em mostrar/esconder lista e gravar o valor selecionado. Fazer a separação de responsabilidades seria um pouco mais complicado, mas ainda sim possível.