O leitor aprenderá a fazer uso do framework de componentes do Framework7 para navegar entre páginas, criar efeitos de transição, exibir dados de um produto individualmente, bem como navegar tal produto até a página de carrinho de compras do nosso app de loja virtual. Veremos também como manipular novos componentes como o picker (combo suspenso de opções), o preloader (efeito de carregamento) e os cards (para exibir dados em forma de cartões).
Na segunda parte deste artigo trabalhamos essencialmente com a criação do nosso serviço e dos métodos de fachada que servirão para intermediar o app mobile em si e a lógica de negócios (que inclui o próprio banco de dados) no servidor. Esse tipo de infraestrutura é necessário independente da linguagem de programação escolhida e é importante desde já que o leitor entenda, sobretudo, como o JavaScript e suas bibliotecas (Framework7, jQuery, etc.) se comunicam com os protocolos web, principalmente os baseados no Restful.
Nessa parte, trataremos de explorar mais recursos do Framework7, como componentes, funções utilitárias JavaScript, etc., com o intuito de auxiliar o jQuery no trabalho de dinamização do conteúdo. Veremos como criar a exibição de um produto selecionado pelo usuário, um mecanismo de parcelamento do valor total, bem como navegar entre páginas, como para a de checkout (carrinho de compras), que exibirá um picker com as opções de pagamento e a quantidade de produtos possível de ser selecionada pelo usuário.
Mas antes de dar prosseguimento, é aconselhável que o leitor atualize a sua versão do Framework7 (seção Links), visto que desde a primeira parte do artigo já foram lançadas atualizações, bem como novos recursos.
Manipulando as imagens do app
Antes de dar prosseguimento ao desenvolvimento do app, precisamos ajustar a forma como as imagens são manipuladas pelo mesmo, já que até o momento só as estamos exibindo de forma local, criando tags <img> e setando seus atributos src para URLs externas. Para isso, vamos usar a própria aplicação web que criamos na segunda parte, a qual possibilita não somente acesso às URLs dos serviços, como também de quaisquer recursos web (páginas HTML – estáticas ou dinâmicas, imagens, arquivos de CSS, JS, etc.). Façamos um pequeno teste: dentro da pasta WebContent do projeto web, crie uma nova pasta produtos e dentro desta, uma outra pasta img. Essa será a pasta que usaremos para salvar todas as imagens referentes aos produtos na loja virtual. Em seguida, salve uma imagem qualquer (pode usar a mesma de geladeira que usamos até então) dentro deste diretório. Ao fazer isso, você terá a mesma disponível através da seguinte URL:
http://192.168.56.1:8080/lojavirtual-web/produtos/img/geladeira-preta-brastemp.png
E é assim que vamos acessá-las daqui para frente. O leitor talvez esteja se perguntando como as imagens serão salvas dentro do projeto, todavia, considerando que você deva buscá-las de uma regra de negócio especifica, não vamos tratar desse ponto para não fugir ao foco do artigo.
Para o aplicativo, especificamente, precisaremos salvar não somente uma, mas várias imagens de um mesmo produto, logo, não podemos salvar suas referências na base de dados na tabela de tb_produto, já que a mesma salva apenas um dado diferente por produto cadastrado. Isso requer um relacionamento com uma segunda tabela, que chamaremos de tb_produto_imagens. Veja na Figura 1 o diagrama entidade relacionamento entre as duas referidas entidades.
Figura 1. Relacionamento entre as entidades Produto e Produto_Imagem.
A nova tabela consta de quatro campos, a saber:
· O identificador, auto incremento, de tipo inteiro;
· A coluna path, que se encarregará de salvar o caminho relativo da imagem (diz-se relativo porque consideraremos apenas o valor após a pasta /WebContent/);
· Uma coluna booleana (isPrincipal) para verificar se a imagem atual é a principal, ou seja, que será exibida na tela de listagem inicial;
· E a coluna de chave estrangeira, que conecta a tabela com a tb_produto.
Ainda precisamos executar o script SQL que cria a nova tabela de imagens do produto e seu relacionamento com a de produto. Para isso, execute o script da Listagem 1 no MySQL.
Listagem 1. Script SQL para criação da nova tabela.
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
CREATE TABLE IF NOT EXISTS `loja_phonegap`.`tb_produto_imagens` (
`id_image` INT(11) NOT NULL AUTO_INCREMENT,
`path` VARCHAR(150) NOT NULL,
`isPrincipal` TINYINT(1) NOT NULL DEFAULT 0,
`id_produto` INT(11) NOT NULL,
PRIMARY KEY (`id_image`),
INDEX `fk_tb_produto_imagens_tb_produto_idx` (`id_produto` ASC),
CONSTRAINT `fk_tb_produto_imagens_tb_produto`
FOREIGN KEY (`id_produto`)
REFERENCES `loja_phonegap`.`tb_produto` (`id_produto`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
Isso será o bastante para atualizar a base com a estrutura física. Agora, precisamos converter as imagens de cada produto de teste em dados inteligíveis para o MySQL. Dentro do projeto final que você encontra no topo do artigo, estará disponível a pasta produtos/img/ que citamos, com todas as imagens de que precisamos. Copie-a para o seu projeto. O leitor pode ficar à vontade para definir os nomes das imagens, bem como a forma que irá recuperá-las.
Para finalizar, vamos inserir os novos dados das imagens no banco via script SQL apresentado na Listagem 2.
Listagem 2. Script SQL para inserção de dados na nova tabela.
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/tv_led_32_01.jpg', true, 1);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/tv_led_32_02.jpg', false, 1);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/tv_led_32_03.jpg', false, 1);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/maquina_lavar_01.jpg', true, 2);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/maquina_lavar_02.jpg', false, 2);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/maquina_lavar_03.jpg', false, 2);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/camera_canon_01.jpg', true, 3);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/camera_canon_02.jpg', false, 3);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/camera_canon_03.jpg', false, 3);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/samsung_notebook_01.jpg', true, 4);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/samsung_notebook_02.jpg', false, 4);
INSERT INTO TB_PRODUTO_IMAGENS(PATH, ISPRINCIPAL, ID_PRODUTO) VALUES ('produtos/img/samsung_notebook_03.jpg', false, 4);
Veja que já nos preocupamos em definir os ids de cada produto, relacionando corretamente os quatro tipos (TV, notebook, etc.) inseridos na pasta de produtos do projeto web. Você precisa se certificar de que os ids na sua tabela são os mesmos definidos no script. Às vezes, executamos um script mais de uma vez, ou removemos um dado apenas para teste, e acabamos incrementando esse id.
Criando os serviços de imagens
Uma vez com a estrutura da base pronta e as imagens dos produtos relacionadas, podemos efetuar a sua busca na base e seu retorno pelo método de listagem de produtos no serviço. Para isso, criemos antes uma nova classe de nome ProdutoImagem, que conterá os dados da tabela tb_produto_imagens no lado Java. Inclua o código da Listagem 3 na mesma. Veja que também adicionamos a anotação @XmlRootElement para se encarregar de converter o objeto Java para JSON e vice-versa.
Listagem 3. Código da entidade de ProdutoImagem.
package br.edu.devmedia.entidade;
@XmlRootElement
public class ProdutoImagem {
private int id;
private String path;
private boolean principal;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isPrincipal() {
return principal;
}
public void setPrincipal(boolean principal) {
this.principal = principal;
}
}
Certifique-se também de incluir a nova dependência da lista de ProdutoImagem na classe Produto, criando também seus getters e setters:
private List<ProdutoImagem> imagens;
// getters e setters
Apesar de estarmos criando uma composição dentro da classe Java, inserindo uma lista de objetos dentro de outro, a Jersey API se encarrega de fazer as conversões automaticamente. Em seguida, precisamos também atualizar a classe ProdutoDAO para carregar agora a lista de produtos com a respectiva lista de imagens associadas. Para isso, modifique o seu conteúdo para o demonstrado na Listagem 4.
Listagem 4. Método refatorado de listagem dos produtos em ProdutoDAO.
01 public class ProdutoDAO {
02
03 public List<Produto> listarProdutos() {
04 List<Produto> produtos = new ArrayList<Produto>();
05 Connection con = DBConfig.getConexao();
06
07 try {
08 StringBuilder sql = new StringBuilder("SELECT * FROM TB_PRODUTO");
09
10 PreparedStatement stm = con.prepareStatement(sql.toString());
11 ResultSet resultSet = stm.executeQuery();
12 while (resultSet.next()) {
13 Produto produto = new Produto();
14 int idProduto = resultSet.getInt("ID_PRODUTO");
15
16 produto.setId(idProduto);
17 produto.setTitulo(resultSet.getString("TITULO"));
18 produto.setDescricao(resultSet.getString("DESCRICAO"));
19 produto.setPreco(resultSet.getDouble("PRECO"));
20 produto.setImagens(listarImagensPorId(idProduto));
21
22 produtos.add(produto);
23 }
24 con.close();
25 } catch(Exception ex) {
26 ex.printStackTrace();
27 }
28
29 return produtos;
30 }
31
32 private List<ProdutoImagem> listarImagensPorId(int idProduto) {
33 List<ProdutoImagem> imgProdutos = new ArrayList<ProdutoImagem>();
34 Connection con = DBConfig.getConexao();
35
36 try {
37 StringBuilder sql = new StringBuilder("SELECT * FROM TB_PRODUTO_IMAGENS WHERE ID_PRODUTO = ?");
38
39 PreparedStatement stm = con.prepareStatement(sql.toString());
40 stm.setInt(1, idProduto);
41
42 ResultSet resultSet = stm.executeQuery();
43 while (resultSet.next()) {
44 ProdutoImagem imgProduto = new ProdutoImagem();
45 imgProduto.setId(resultSet.getInt("ID_IMAGE"));
46 imgProduto.setPath(resultSet.getString("PATH"));
47 imgProduto.setPrincipal(resultSet.getBoolean("ISPRINCIPAL"));
48
49 imgProdutos.add(imgProduto);
50 }
51 con.close();
52 } catch(Exception ex) {
53 ex.printStackTrace();
54 }
55
56 return imgProdutos;
57 }
58
59 }
Vejamos algumas mudanças importantes na mesma:
· No método listarProdutos() da linha 3, setamos os valores das imagens do produto via método setImagens() (linha 20), que, por sua vez, carregará a lista de imagens a partir do método listarImagensPorId(). Esse tipo de procedimento se faz necessário porque os dados estão em outra tabela, logo, precisamos passar a coluna de chave primária, a qual estará conectada diretamente à coluna de chave estrangeira na tabela de produto_imagens;
· Na linha 32, criamos o novo método de listagem das imagens: listarImagensPorId() (é interessante que o leitor dê sempre nomes intuitivos aos seus métodos para facilitar o entendimento no futuro). Ele recebe como parâmetro o id do produto a ter suas imagens pesquisadas e retorna a lista das referidas imagens;
· A estrutura do método é muito semelhante à do anterior, exceto pela nova query que estamos consultando, que agora tem uma cláusula WHERE para especificar quais linhas da tabela devem ser trazidas.
Na linha 40 setamos o valor do id do produto (enviado por parâmetro no método anterior) via método setInt() (também temos um para cada tipo de dado no Java).
Nesse método, veja que passamos um valor numérico como primeiro parâmetro, isso porque na query tivemos uma interrogação no lugar onde deveria ter sido inserido o id do produto.
Esse é um tratamento de segurança interno da API do Java para garantir que nenhum SQL seja “injetado” maliciosamente nas nossas queries e acabe por danificar a estrutura do banco. Portanto, para cada interrogação inserida na consulta, informamos o valor numérico correspondente à ordem da mesma (começando de 1) em cada método set() do PreparedStatement;
· Nas linhas 43 a 50 iteramos sobre a lista de resultados e criamos vários objetos de imagem, adicionando um a um na lista retornada.
Pronto, agora só temos de ajustar o front-end do aplicativo para lidar com os novos valores passados. Para tanto, como vamos passar a usar o endereço da aplicação web em lugares diferentes no código JavaScript, é interessante criarmos uma constante global e reusar esse endereço nas demais partes do código, assim asseguramos que uma provável mudança futura de endereço (IP ou porta) não acarrete no não funcionamento do aplicativo. Crie, portanto, a seguinte linha de código no início do seu arquivo index.js:
var WS_HOME_WEB = 'http://192.168.56.1:8080/lojavirtual-web/';
E modifique a função listarProdutos() no mesmo arquivo para o código demonstrado na Listagem 5.
Listagem 5. Novo código que recebe a lista de produtos no JavaScript.
01 function listarProdutos() {
02 $.postJSON(WS_HOME_WEB + 'rest/produto/list/', {}, function(produtos) {
03 $(produtos).each(function() {
04 var clone = $('#produtos li:first').clone().show();
05 clone.find('.item-title').html(this.titulo);
06 clone.find('.item-text').html(this.descricao);
07 clone.find('.item-after').html('R' + formatCurrency(this.preco));
08 var pathImg;
09 $(this.imagens).each(function() {
10 if (this.principal) {
11 pathImg = this.path;
12 }
13 });
14 clone.find('.item-media img').attr('src', WS_HOME_WEB + pathImg);
15 $('#produtos').append(clone);
16 });
17 }).error(function(jqXHR, error, errorThrown) {
18 if ...