Populando um elemento select

O elemento select é bastante utilizado em sistemas pois permite que o usuário selecione uma opção entre uma lista de opções. Você já deve estar familiarizado com esse elemento, de uma olhada na estrutura básica de um select:

1
<select name="estado">
2
<option value="">Estado</option>
3
<option value="SP">São Paulo</option>
4
<option value="SC">Santa Catarina</option>
5
<option value="PR">Paraná</option>
6
</select>

O elemento é formado por uma tag select que indica o ínicio e o fim do elemento e uma lista de tags option que representam as opções que o usuário pode selecionar. Cada tag option possui um atributo value que indica o valor que será enviado para o servidor quando o usuário selecionar a opção. O texto que aparece dentro da tag option é o texto que será exibido para o usuário.

Porém, como você pode perceber, a lista de opções é estática, ou seja, ela não é gerada dinamicamente, caso queira alterar a lista é preciso alterar o código HTML. Para que a lista seja gerada dinamicamente é preciso que os dados da lista venham do servidor.

Primeiramente vamos criar uma classe que representará a lista de opções as quais o usuário poderá selecionar:

1
public class Categories {
2
public static final String[] CATEGORIES = new String[] {"Desktop", "Notebook", "Smartphone"};
3
}

Essa classe possui um atributo estático que é um array de String que contém as opções que o usuário poderá selecionar.

Existem maneiras mais elegantes de fazer isso, porém, para manter o foco no Spring Boot, vamos manter dessa forma.

Agora precisamos adicionar ao nosso formulário o elemento select que irá exibir as opções para o usuário. Para isso, na página product-form.html adicione o seguinte código dentro do formulário:

1
<label for="category">Categoria</label>
2
<select name="category" id="category">
3
<option value="">Selecione uma categoria</option>
4
<option value="Desktop">Desktop</option>
5
<option value="Notebook">Notebook</option>
6
<option value="Smartphone">Smartphone</option>
7
</select>

So você rodar a aplicação irá perceber que o elemento select já está sendo exibido, porém a lista de opções continua estática e não dinâmica. Para tornar a lista dinâmica é preciso que a visão tenha acesso aos dados da classe Categories. Para isso, altere o método create`` da classe ProductController` para que ele envie a lista de categorias para a visão:

1
@GetMapping("/products/create")
2
public String create(Model model) {
3
Product product = new Product();
4
model.addAttribute("product", product);
5
model.addAttribute("categories", Categories.CATEGORIES);
6
return "product/create";
7
}

Agora precisamos alterar a visão para que ela utilize a lista de categorias que foi enviada pelo controlador. Para isso, altere o elemento select para que ele utilize a lista de categorias:

1
<label for="category">Categoria</label>
2
<select name="category" id="category">
3
<option value="">Selecione uma categoria</option>
4
<option th:each="category : ${categories}" th:value="${category}" th:text="${category}"></option>
5
</select>

Rode a aplicação e você verá que a lista de opções está sendo gerada dinamicamente. Para confirmar isso, adicione uma nova categoria na classe Categories e rode a aplicação novamente, você verá que a nova categoria aparece automaticamente na lista de opções.

Analisando o código acima, você pode perceber que o elemento option possui um atributo th:each que indica que o elemento option será repetido para cada elemento da lista ${categories}. Dentro do elemento option ainda existem dois atributos th:value e th:text que indicam respectivamente o valor que será enviado - atributo value do elemento - para o servidor e o texto que será exibido para o usuário.

Você pode conferir o HTML final que está sendo gerado utilizando a opção de verificar o código fonte da página no navegador. Faça isso clicando com o botão direito do mouse na página e selecionando a opção Ver código fonte da página ou pelo atalho do teclado Ctrl + U. O código será similar ao seguinte:

1
<label for="category">Categoria</label>
2
<select name="category" id="category">
3
<option value="">Selecione uma categoria</option>
4
<option value="Desktop">Desktop</option>
5
<option value="Notebook">Notebook</option>
6
<option value="Smartphone">Smartphone</option>
7
</select>

Veja que o HTML gerado é exatamente o mesmo que foi escrito no começo do artigo, com os valores do atributo value e o texto que será exibido para o usuário sendo gerados dinamicamente.

Sempre que você estiver com dúvidas sobre o HTML gerado, utilize a opção de verificar o código fonte da página no navegador. Isso irá te ajudar a entender o que está acontecendo.

Se você tentar fazer a submissão do formulário verá que o produto continua sendo salvo normalmente, porém, o atributo category não está sendo utilizado para nada. Isso acontece porque o atributo category ainda não existe na classe Product e nem no banco de dados. Para resolver isso, será preciso adicionar o atributo category na classe Product. Termine o código adicionando o atributo category na classe Product e alterando os demais locais afetados por essa mudança.

Outra parte importante é na página de atualização do formulário. Nessa página os dados do produto já vem previamente populados no formulário e se você observar o campo de categoria sempre está vindo a primeira opção e não a categoria cadastrada no produto.

Isso acontece porque para que uma das opções esteja selecionada por padrão é necessário informar qual das tags option deve ser selecionada. Para isso se utiliza o atributo selected do HTML. Para facilitar o desenvolvimento o Thymeleaf fornece o dialeto th:selected.

Agora para saber qual das options deve ser selecionada, basta fazer uma comparação entre o que está no campo product.category do objeto Product com a lista de categorias em category. Veja como irá ficar o código final:

1
<label for="category">Categoria</label>
2
<select name="category" id="category">
3
<option value="">Selecione uma categoria</option>
4
<option th:each="category : ${categories}"
5
th:value="${category}"
6
th:text="${category}"
7
th:selected="${product.category == category}">
8
</option>
9
</select>