Exibindo detalhes de um produto

Agora que você já tem uma noção de como mostrar informações em uma tela e manipular dados de um formulário podemos melhorar a aplicação para exibir mais informações a respeito de um produto.

Quando navegamos em sites e começamos a observar, podemos encontrar um padrão entre as páginas. Por exemplo, em um site de e-commerce, a primeira página que acessamos é a página inicial, que geralmente exibe uma lista de produtos. Ao clicar em um produto, somos redirecionados para uma página que exibe mais informações sobre o produto. Essa página é podemos chamar de detalhes do produto.

A ideia dessa página é exibir informações detalhadas sobre aquele item em específico. Veja você mesmo, acesse qualquer site de e-commerce e clique em um produto. Você será redirecionado para uma página que exibe mais informações sobre aquele produto. Caso esteja sem lembranças de algum site, aqui estão alguns e-commerces famosos do Brasil: Petz, Kabum. Acesse um desses sites e clique em um produto para ver como é a página de detalhes do produto.

Ver depois se encontra a lista de ecommerces por categoria.

Pra conseguirmos ter esse comportamento precisamos de uma página que irá mostrar detalhes de um produto. Em nosso exemplo temos apenas três campos representando o produto, sendo eles, nome, descrição e preço. Porém, em um e-commerce real, teríamos muito mais informações sobre o produto, como por exemplo, a quantidade de itens em estoque, o peso do produto, a categoria do produto, etc. Mas para aprendermos aqui, vamos nos ater apenas aos três campos que temos.

Precisamos criar então uma nova página, responsável por mostrar os detalhes desse produto. Para isso, vamos criar uma nova rota chamada /products/show, essa rota ficará responsável de chamar o arquivo show.html que exibirá as informações do produto. Como já vimos podemos passar um objeto para que a visão possa mostrar os dados desse objeto na tela. Então vamos fazer isso, em seu ProductController.java adicione o seguinte método:

1
@GetMapping("/products/show")
2
public String show(Model model){
3
Product product = new Product("Dell", "Laptop", 1000);
4
model.addAttribute("product", product);
5
return "show";
6
}

Crie um novo arquivo show.html na pasta de templates e adicione o conteúdo:

1
<div class="card">
2
<h2 th:text="${product.name}"></h2>
3
<p th:text="${product.description}"></p>
4
<p th:text="${product.price}"></p>
5
</div>

Agora acesse a url http://localhost:8080/products/show e você verá os dados do produto sendo exibidos na tela com sucesso. Porém, como você pode ver, os dados estão sendo exibidos de forma estática, ou seja, sempre que acessarmos a rota /products/show os dados do produto serão os mesmos. Isso acontece porque sempre estamos passando o memso objeto Product. Essa não é a ideia, queremos que cada produto tenha sua própria página de detalhes. Para isso, precisamos que a rota de detalhes do produto receba um parâmetro que identifique o produto que queremos exibir, geralmente esse parâmetro é o id do produto.

Passando parâmetros para a rota

Quando acessamos uma URL, podemos passar parâmetros para essa URL. Por exemplo, quando acessamos a URL http://localhost:8080/products/show estamos acessando a rota /products/show e não estamos passando nenhum parâmetro para essa rota. Porém, se acessarmos a URL http://localhost:8080/products/show?id=1 estamos acessando a mesma rota /products/show porém estamos passando um parâmetro id com o valor 1.

A URL é composta de diversas partes que tem seus próprios significados. Até agora utilizamos alguns pedaços delas, tais como o protocolo, o domínio, a porta e o caminho. Por exemplo, em http://localhost:8080/products/show temos o protocolo http, o domínio localhost, a porta 8080 e o caminho /products/show.

Logo após o caminho, a URL também aceita parâmetros que chamamos de query parameters, ou parâmetros de consulta. Esses parâmetros são informações qeu são enviadas via URL para o servidor e seguem um formato rígido de chave e valor.

Para identificar que iniciam-se os parâmetros temos o caractere ?. Logo após dele temos conjuntos formados por chaves e valores separados pelo caractere =. Caso quisermos enviar mais de um parâmetro, os separamos pelo caractere &. Dessa maneira, imagine que queremos enviar os dados de nome e cor para uma página chamada show.html. A URL ficaria da seguinte maneira:

1
http://localhost:8080/products/show?name=Notebook&color=black

Nesse caso, temos dois parâmetros, name e color. O primeiro parâmetro tem o valor Notebook e o segundo parâmetro tem o valor black. Isso permite que o servidor receba esses parâmetros e possa fazer alguma coisa com eles. No nosso caso, queremos que o servidor receba o parâmetro que possa identificar o produto que queremos acessar, esse parametro costuma ser um identificar único do produto, ou o ID do produto.

Ótimo mas como podemos receber esse parâmetro que veio na URL para que de fato posssamos fazer alguma coisa com essa informação? Para isso, vamos precisar alterar o rotas do nosso ProductController.java para que possamos receber esse parâmetro. Vamos alterar a rota de detalhes do produto para que ela receba um parâmetro chamado name:

1
@GetMapping("/products/show")
2
public String show(Model model, @RequestParam("name") String name){
3
System.out.println("parâmetro recebido via URL: " + name);
4
Product product = new Product("Dell", "Laptop", 1000);
5
model.addAttribute("product", product);
6
return "show";
7
}

Perceba que fizemos duas alterações no método. A primeira foi fazer o mapeamento do parâmetro a ser recebido com a anotação @RequestParam() que recebe a chave definida na URL - lembre-se que você define esse nome de chave - e o tipo de dado que você quer receber. No nosso caso, queremos receber uma String. A segunda alteração foi adicionar um System.out.println() simplesmente para que possamos ver o valor do parâmetro que estamos recebendo por enquanto. Rode a aplicação novamente e acesse a rota http://localhost:8080/products/show?id=1 e você verá que o parâmetro está sendo recebido com sucesso. Troque o valor do id para outros valores e verifique se o valor está sendo exibido no console.

Tente acessar a rota sem passar o parâmetro ou mesmo passando um parâmetro diferente do especificado. Você irá receber um erro 400, que significa que a requisição está incorreta. Isso acontece porque estamos esperando um parâmetro name e não estamos recebendo nenhum parâmetro ou estamos recebendo um parâmetro diferente do esperado.

Agora chegou a hora de personalizar o produto que queremos exibir, já que temos uma informação sobre o produto, podemos usá-la pra buscar o produto correto na lista de produtos e exibir esse produto em questão. Para isso precisaremos encontrar o produto na lista com base em seu nome. Pense por um minuto como você poderia implementar isso.

1
private Product findProductByName(String name) {
2
for (Product product : products) {
3
if (product.getName().equals(name)) {
4
return product;
5
}
6
}
7
return null;
8
}

Aqui tem uma implementação simples que percorre a lista de produtos e retorna o produto caso encontre e o valor null caso não encontre o produto. Agora podemos alterar o método do controlador para usar esse método de pesquisa.

1
@GetMapping("/products/show")
2
public String show(Model model, @RequestParam("name") String name){
3
System.out.println("parâmetro recebido via URL: " + name);
4
Product product = findProductByName(name);
5
model.addAttribute("product", product);
6
return "show";
7
}

Agora se você acessar a rota http://localhost:8080/products/show?name=HP você verá que o produto está sendo exibido corretamente. Porém, se você acessar a rota http://localhost:8080/products/show?name=Smartphone você receberá um erro. O erro acontece porque não foi encontrado o produto na lista e então o método retorna null. Esse valor nulo é enviado então para a visão como se fosse um produto e a visão tenta exibir os dados desse produto, porém, como o produto é nulo, o Thymeleaf não consegue exibir os dados e então ocorre um erro.

Por enquanto vamos tratar esse caso na visão para usarmos o que aprendemos em lições passadas. Vamos mostrar o produto apenas se existir esse produto, caso contrário mostramos uma mensagem genérica dizendo que o produto não foi encontrado.

1
<div class="card">
2
<div th:if="${product != null}">
3
<h2 th:text="${product.name}"></h2>
4
<p th:text="${product.description}"></p>
5
<p th:text="${product.price}"></p>
6
</div>
7
<p th:unless="${product == null}">
8
Produto não encontrado!
9
</p>
10
</div>

Teste novamente seu código e verá que agora temos uma página personalizada para cada produto existente na lista. Aliás você pode cadastrar um novo produto e novamente acessar a rota de detalhes passando o nome do produto que você acabou de cadastrar e verá que o produto está sendo exibido corretamente.