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.