Prévia do material em texto
Camada de visão
Utilização de ferramentas como Thymeleaf e Bootstrap na construção da camada View de uma arquitetura
MVC para Web, em sistemas baseados no framework Spring, viabilizando a criação de interfaces
responsivas e design profissional, com pouco esforço de programação.
Prof. Denis Cople
1. Itens iniciais
Propósito
Definir interfaces de sistemas Web criados com o framework Spring, com grande produtividade, obedecendo ao
padrão arquitetural MVC, além da garantia de responsividade, por meio de ferramentas como Thymeleaf e
Bootstrap, as quais viabilizam a criação de interfaces com design profissional.
Preparação
Antes de começar, é necessário configurar o ambiente com a instalação do JDK, Spring Tools Suite e NetBeans
completo, em versão com suporte a projetos Maven, além de adicionar o servidor Tomcat EE na instalação da
IDE. Também será necessário acessar a internet tanto para testes quanto para o download de artefatos via
Maven. Por fim, acesse os arquivos dos projetos desenvolvidos.
Objetivos
Reconhecer as sintaxes básicas utilizadas na Web para definição de
páginas.
Aplicar o Thymeleaf por meio de templates para definição da camada View
do MVC.
Empregar o Bootstrap na construção da interface para garantia de
produtividade.
Introdução
Abordaremos os elementos necessários para a construção de interfaces de usuário robustas, com
responsividade e aspecto profissional, por meio de ferramentas utilizadas amplamente no mercado. Nosso foco
inicial será na compreensão das sintaxes básicas para a construção de páginas, incluindo HTML, Java Script e
CSS, além da utilização de JQuery UI.
Na sequência, veremos como pode ser feita a integração com sistemas Spring Web, por meio de templates do
Thymeleaf, dentro de uma arquitetura MVC, de forma muito simples e concisa.
Por fim, aplicaremos o framework Bootstrap em um sistema cadastral completo, com a definição de telas
alinhadas aos melhores princípios de design e responsividade exigidos pelo mercado, além de incluir os
componentes de segurança necessários para definir um modelo de autenticação básico, integrando-se às
interfaces criadas com muita fluidez.
•
•
•
https://stecine.azureedge.net/repositorio/00212ti/03593/docs/Projetos.zip
https://stecine.azureedge.net/repositorio/00212ti/03593/docs/Projetos.zip
1. Implementação de uma página simples da web
HTML 5
A linguagem HTML (Hypertext Markup Language) fornece a base estrutural para as páginas disponibilizadas na
Internet. Visa estruturar a página com base em tags, ou etiquetas, em que temos uma Q, segundo o perfil de
exibição, formulários de cadastro e elementos para a navegação entre páginas. Inclusive, o nome hipertexto se
refere à capacidade de navegação ao longo do texto.
Sintaxe
A sintaxe XHTML equivale ao HTML 4.0 Strict, que evoluiu para o HTML 5.
Toda página apresenta um cabeçalho (head), em que temos o título (title) apresentado no topo da janela do
navegador, ligações (link) com recursos externos, folhas de estilo e demais elementos de uso geral. Na
sequência, deve ser definido o corpo da página (body), contendo a parte visual de nosso documento.
No corpo do documento, temos elementos estruturais de texto, como a utilização de fontes padronizadas para
títulos, delimitação de parágrafos e criação de listas. Embora o HTML não diferencie maiúsculas e minúsculas, as
páginas serão validadas na W3C apenas se as tags forem escritas com letras minúsculas. Vejamos algumas tags:
Tags Utilização
h1, h2, h3,
h4, h5, h6
Aplica a formatação padronizada para títulos, em que h1
utiliza fonte maior, e vai diminuindo com a numeração.
br, hr, p Enquanto p define um parágrafo, br é uma quebra de linha
simples, e hr efetua a quebra com o desenho de uma linha
horizontal.
ul, ol, li Listas com marcadores numéricos são definidas com o uso de
ol, enquanto as simbólicas utilizam ul. Em ambos os casos,
itens são definidos com li.
Tabela: Tags básicas HTML
Denis Cople.
Tags básicas
Vejamos uma aplicação no Exemplo001.html.
python
A tag meta permite definir metadados, aqui definida a página de código UTF-8, permitindo o uso de acentuação
em palavras da língua portuguesa. No HTML, também temos símbolos especiais, chamados de entidades, que
são iniciadas com ‘e comercial &;’ e fechadas com ponto e vírgula, como b, representando o símbolo de
copyright.
Outro elemento é o comentário utilizado no HTML, iniciado com sinal de exclamação e duas subtrações, e
fechado com duas subtrações.
Abrindo a página no navegador, teremos o resultado a seguir. Para facilitar os testes, podemos criar um aplicativo
Web, no NetBeans, e acrescentar a página HTML.
Primeira página
UNIVERSIDADE ©2022
Nosso primeiro exemplo, apenas com
estruturas básicas
e uma quebra de linha.
Departamentos:
Desenvolvimento
Financeiro
•
•
Visualização de Exemplo001.html
Tabelas
Nas tabelas, temos uma divisão em linhas, e cada linha com suas respectivas colunas, definindo células de
informação. Com base em atributos como colspan e rowspan, as células podem ser combinadas, e ainda temos a
possibilidade de uma linha de cabeçalho. Vejamos algumas tags:
Tags Utilização
table Define uma tabela, podendo ter borda e largura
especificados.
tr Define uma linha da tabela.
td, th Definem células de informação, ou colunas, na linha, sendo
que th é utilizada no cabeçalho.
thead,
tbody,
tfoot
Definem trechos de cabeçalho, corpo e rodapé na tabela, para
que ocorra o reconhecimento a partir de outras ferramentas.
Tabela: Tags para desenho de tabelas.
Denis Cople.
O uso de tabelas é indicado para a exibição de dados tabulares, como a seguir.
python
A tabela foi criada com alguns atributos visuais, incluindo a borda de espessura 1 e utilização de 100% do espaço
horizontal disponível. Note o uso de colspan na primeira e última linhas, em que células subsequentes são
mescladas, com base no valor definido por meio do atributo.
Vejamos a saída da página Exemplo002.html.
Visualização de Exemplo002.html
Formulários
Nos formulários, podemos contar com os componentes mais tradicionais das interfaces de usuário, como botões,
caixas de texto e caixas de seleção. Vejamos algumas tags:
Tags Utilização
form Define um formulário, sendo necessário indicar o endereço
para onde as informações serão enviadas e o método HTTP
utilizado.
Nome Telefones
Ana 111-1111 111-2222
Luiz 222-1111
Total de contatos: 2
Tags Utilização
Input Desenha um componente de entrada, em que o atributo type
permite a escolha de sua aparência, podendo ser text, button,
checkbox, radio, reset ou submit, em que os últimos são os
botões de reinício e envio.
Select,
datalist
Expressam elementos de entrada multivalorados, ou seja, as
caixas de seleção em que os valores são expressos na forma
de listas.
Textarea Define um componente de entrada para textos maiores,
permitindo a quebra de linhas.
Label Utilizado para descrever um campo do formulário. É muito útil
para otimizar a visualização em dispositivos móveis.
Fieldset,
legend
Utilizados para configurar grupos de campos de entrada, com a
adição de uma legenda e uma borda.
Tabela: Tags para criação de formulários.
Denis Cople.
Para textos maiores, devemos utilizar o textarea, que permite quebra de linhas, sendo configurado em termos do
número de linhas e colunas.
Quanto a elementos multivalorados, como listas, temos as opções select e datalist, ambos definindo os
elementos da lista por meio de option. No entanto, o datalist deve ser utilizado para pequenos conjuntos de
valores fixos, enquanto o select, que permite o uso de uma chave e um texto para a opção, reflete melhor um
conjunto dinâmico.
Vejamos componentes no Exemplo003.html.
python
Nosso formulário tem como destino um endereço fictício, associado por meio de action, usando o métodocontrolador, obtendo cada um
deles na variável depto, e utilizamos cada ocorrência para a construção de uma linha da tabela. Os valores de
cada campo da entidade são acessados com o uso de cifrão, utilizado tanto para o preenchimento das células
de valores quanto na definição dos links de forma dinâmica.
Já podemos executar o aplicativo e acessar o endereço http://localhost:8080/departamentos, obtendo a saída
a seguir:
Departamentos
Novo Departamento
IdDepto Nome Opcoes
Excluir
Alterar
Listagem dos departamentos .
Agora, vamos definir o segundo template, com o nome deptodata.html.
python
Nosso formulário utilizará o objeto depto, fornecido pelo controlador, por meio de th:object, e iniciará a ação
definida por th:action para a rota /departamentos/cadastro, no modo POST. Observe a utilização de asterisco
para acesso aos campos do objeto, com base em th:field, em cada um dos campos de nosso formulário.
Como a chave primária é gerada automaticamente para o departamento, ela é colocada em um campo
escondido (hidden), evitando a edição pelo usuário. Já o campo referente ao nome será apresentado em branco
para a inclusão, ou com o valor corrente na alteração.
Dados do Departamento
Nome
Cadastrar
Já podemos acessar os diversos links oferecidos pela tela de listagem, pois o controlador está preparado para
todas as rotas solicitadas. Clicando na opção "Novo Departamento", abriremos o formulário de dados em branco.
Formulário para inclusão e alteração de departamento.
Templates para funcionários
Vamos definir as telas para funcionários, iniciando com o arquivo funclist.html.
python
Funcionários
Novo Funcionário
Matricula Nome Cadastro Depto Opções
Excluir
Alterar
Há muita similaridade com a tela de listagem de departamentos, agora com as rotas iniciadas por /funcionários,
mas temos algumas pequenas novidades. Note o uso da macro, precedida por cerquilha, com o nome dates.form
at, que permite formatar a data de cadastro, bem como o acesso ao nome do departamento de forma direta, a
partir do atributo de relacionamento existente na classe Funcionario.
Acessando o endereço http://localhost:8080/funcionarios, podemos testar a tela, obtendo a saída a seguir:
Listagem de funcionários.
O formulário para funcionários será definido no template funcdata.html.
python
Nesse caso, temos a chave primária digitada pelo usuário. Há também a definição de um campo de seleção para
o campo departamento, em que as opções são preenchidas a partir da lista de departamentos com o nome dept
os, fornecida pelo controlador. Observe a utilização de th:each, para a iteração, th:value, para definição do valor
fornecido pela opção, e th:text, para o texto apresentado.
Com o template criado, acessando os links para alteração ou inclusão, teremos a exibição da tela a seguir:
Dados do Funcionário
Matricula
Nome
Departamento
Cadastrar
Formulário para inclusão e alteração de funcionário.
Teoria na prática
Utilizando Thymeleaf com Spring Boot
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Vem que eu te explico!
Os vídeos a seguir abordam os assuntos mais relevantes do conteúdo que você acabou de estudar.
Características do Thymeleaf
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Filtro de Interceptação
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Verificando o aprendizado
Questão 1
As diretivas utilizadas no Thymeleaf permitem uma integração muito simples entre o template HTML e o
contexto do framework Spring, possibilitando recuperação de valores e reutilização de fragmentos, entre
diversas outras possibilidades. Se quisermos recuperar o nome da empresa a partir de um parâmetro global,
para que seja apresentado em cada página, qual símbolo seria utilizado pela diretiva de recuperação?
A
Arroba
B
Cifrão
C
Til
D
Asterisco
E
Cerquilha
A alternativa E está correta.
Segundo a Standard Expression Syntax, o uso de cerquilha (#) permite recuperar textos padronizados a
partir de arquivos de propriedades, particularmente com a definição de um arquivo message.properties,
no diretório resources, em que serão colocadas as mensagens do sistema, como pares de chave e valor,
com a recuperação posterior a partir da chave.
Questão 2
Os templates do Thymeleaf utilizam atributos nas tags HTML, definidos em seu namespace, para definir ações
diversas, como controle de visibilidade e substituição de conteúdo para a tag, sendo normalmente adotado o
prefixo th. Se quisermos repetir determinado trecho, para todos os elementos de uma coleção, qual seria o
atributo adotado na tag principal do trecho?
A
Th:each
B
Th:text
C
Th:object
D
Th:action
E
Th:value
A alternativa A está correta.
A utilização de th:each permite repetir a tag e seu conteúdo interno, ou seja, o trecho da página, para
cada objeto da coleção. Em termos práticos, recuperamos uma coleção enviada pelo controlador,
precedida de cifrão, atribuindo a um objeto em cada iteração. O objeto pode ser acessado no conteúdo
interno, recuperando suas propriedades. Um exemplo de utilização desse controle de fluxo seria
th:each="depto: $$ {deptos}".
3. Estudo de caso com Framework Bootstrap
Framework do Bootstrap
O framework Bootstrap permite a definição de interfaces muito agradáveis, com uma estética alinhada aos
padrões de mercado, além de trazer elementos que garantem a responsividade, ou seja, adapta-se a diferentes
plataformas de forma natural. Com o Bootstrap, obtemos um conjunto de componentes responsivos, com visual
moderno, perfeitamente alinhados aos padrões estabelecidos no mercado.
O Bootstrap encapsula o framework JQuery, adicionando diversas funcionalidades por meio de classes CSS.
Podemos baixar o framework no endereço https://getbootstrap.com/. Além de oferecer diversas opções de
formatação para componentes, como botões e caixas de texto, temos a definição de contêineres, muito úteis
para a organização da tela.
Estilos de botões
Para compreender a utilização do Bootstrap, vamos criar o arquivo Exemplo015.html, em um projeto Web do
NetBeans, dando continuidade aos exemplos iniciais do conteúdo.
python
No cabeçalho do arquivo, temos as importações necessárias para uso do Bootstrap, ou seja, a folha de estilo boo
tstrap.min.css e o script bootstrap.bundle.min.js, ambos obtidos a partir do repositório npm, na versão 5.0.2.
Todos os arquivos que utilizarem o framework deverão ter as mesmas importações de nosso exemplo.
Podemos observar a definição da área principal do aplicativo, com a classe container, e diferentes formatações
para os botões, com a classe btn, que pode ser especializada para suas subclasses, como btn-success e btn-
info, modificandoo aspecto dos botões, segundo padrões adotados nas diferentes plataformas, particularmente
Web e móvel.
Abrindo a página em um navegador, teremos o seguinte resultado:
Estilos de Botões
Básico
Padrão
Primário
Sucesso
Informação
Aviso
Perigo
Link
Botões formatados pelas classes do Bootstrap.
Caixas de diálogo modais
O uso de caixas de diálogo modais permite uma comunicação simples e organizada com o usuário. Vamos
explorar essa funcionalidade no arquivo Exemplo016.html.
python
Na primeira parte do código, temos um botão, configurado com o aspecto btn-danger, e dois atributos
especiais, data-bs-toggle e data-bs-target: o primeiro define que será alterada a visibilidade de um elemento
modal, e o segundo indica o modal a partir do id. Em seguida, temos a construção do modal em si, com id myMod
al, por meio de tags div.
Exemplo de Modal
Excluir Registro
Comentário
A divisão de nível mais alto do diálogo é configurada com as classes modal e fade, além de usar um
atributo role, com valor dialog, o que indica uma caixa de diálogo. Em seguida, temos um segundo
nível, configurado como modal-dialog, e um terceiro, do tipo modal-content, que será responsável
pelo conteúdo interno da caixa de diálogo.
O conteúdo do diálogo é composto de três áreas, configuradas como modal-header, para as informações de
título, modal-body, para as informações que serão exibidas, e modal-footer, para a parte inferior do diálogo,
normalmente com alguns botões de resposta.
Os dois botões são configurados para o fechamento do diálogo, por meio do atributo data-bs-dismiss, utilizando
o valor modal. O primeiro é o botão de fechamento padrão para janelas, posicionado no canto superior direito, e
o segundo, um botão padrão, colocado na parte inferior. Podemos observar nosso diálogo sendo ativado:
Diálogo modal com Bootstrap.
Sistema de Grid
O sistema de grid do Bootstrap é outro elemento importante, utilizado na organização da tela a partir de
divisões. Ele segmenta a tela em até 12 colunas, que podem ser combinadas, como na listagem seguinte, que
deverá ficar no arquivo Exemplo017.html.
python
UM
UM
DOIS
QUATRO
QUATRO
QUATRO
OITO
DOZE
>
Temos a primeira linha (row) com duas células de uma coluna (col-sm-1), uma célula de duas colunas (col-sm-2)
e duas de quatro colunas (col-sm-4), a segunda linha com uma célula de quatro colunas e uma de oito colunas (c
ol-sm-8), e apenas uma célula de 12 colunas (col-sm-12) na terceira linha. O resultado pode ser observado a
seguir:
Organização da página pelo sistema de grid.
Componentes para navegação
Os componentes nav e navbar permitem a construção simples de menus para o sistema. Vamos adicionar o
arquivo Exemplo018.html, com o conteúdo a seguir:
python
A barra de navegação do exemplo poderia ser utilizada como menu principal do sistema, pois viabiliza mudança
de páginas e indicação da opção corrente de forma automática. Definimos o componente de navegação no nível
mais alto, utilizando fundo escuro (navbar-dark e bg-dark), além do preenchimento horizontal (navbar-expand-
lg).
O conteúdo do menu é divido em três colunas, segundo os valores de grid do Bootstrap. A primeira contém a
opção principal do menu (navbar-brand), e a última, um botão comum, no estilo btn-danger. Na coluna do meio,
temos uma lista, configurada como navbar-nav, em que cada item é uma opção de navegação (nav-item)
contendo um link (nav-link), sendo a opção corrente configurada no link por meio da classe active.
A execução do exemplo gerará o seguinte resultado:
Cadastro
Departamentos
Funcionarios
Logout
#
#
#
#
#
Menu de navegação criado com NavBar.
Uso de Bootstrap com Thymeleaf
Definição do menu
Utilizar o framework Bootstrap sob o Thymeleaf é uma tarefa simples, pois não requer grandes modificações nos
templates HTML. Nossa preocupação inicial será apenas a inclusão de uma dependência no arquivo pom.xml,
como no fragmento a seguir:
python
Os elementos WebJars são as bibliotecas do Bootstrap oferecidas no formato jar, para permitir a utilização ao
nível do projeto, sem a necessidade de acesso ao repositório remoto. Aqui foi escolhida a versão 5.0.2, devido à
existência de documentação mais completa.
Vamos acrescentar a página menu.html no projeto ExemploTh002, nosso exemplo de CRUD, no diretório templat
es, como as demais páginas, utilizando o conteúdo a seguir:
org.webjars
bootstrap
5.0.2
python
O código utilizado é apenas uma adaptação de nosso menu anterior, mas, agora, com indicação real de
navegação. Esse template será incluído em todas as páginas, aproveitando o recurso de fragmentos oferecido
pelo Thymeleaf.
No primeiro nível, temos a definição do nome do fragmento como menuprincipal, por meio do atributo
th:fragmente. Todos os endereços dos links passam a apontar para as rotas de nosso sistema, sempre
por meio do atributo th:href, com a utilização de arroba para correção.
Temos ainda a indicação da opção ativa com uma inclusão condicional de classe, efetuada no atributo th:classap
pend, com base em uma condição na qual o estilo active será utilizado apenas quando a variável pagina tiver o
valor equivalente ao do teste.
Cadastro
Departamentos
Funcionarios
Logout
Navegação utilizando o menu
Agora, vamos definir uma nova página inicial, com o nome home.html, no diretório templates, de acordo com a
listagem seguinte:
python
Executando novamente o aplicativo, teremos acesso às páginas de departamento com exibição do menu, tanto
para a listagem quanto para o cadastro, e poderemos retornar para a listagem rapidamente com o clique sobre a
opção de menu. Também teremos a indicação da opção de cadastro em uso, com fonte mais clara, como
podemos observar na imagem seguinte:
Listagem de departamentos com inclusão do menu.
O mesmo procedimento deve ser feito para os templates funcdata e funclist, mas, agora, com a utilização do
valor "funcionarios" na variável do fragmento:
python
Alteração das interfaces do CRUD
Templates com listas
Nossa interface está funcional, mas ainda muito simples, o que ajustaremos com a utilização das classes do
Bootstrap. Vamos começar alterando o template deptolist:
python
Departamentos
Novo Departamento
IdDepto Nome Opcoes
Excluir
Alterar
Definimos as classes de botão para os links e a formatação da tabela, em que foram utilizadas as classes table e
table-striped, para expandir o conteúdo na largura da página e trabalhar com alternância de fundo nas linhas.
Temos uma classe col-sm-2, aplicada às células com os links de alteração e exclusão, para que não utilizem um
espaçamento maior do que o necessário.
Todos os links receberam a classe btn, sendo adotado btn-danger para exclusão, e btn-primary para inclusão e
alteração, além da adoção de tamanho pequeno para os botões de alteração e exclusão, por meio da classe btn-
sm. O resultado é apresentado a seguir:
Listagem de departamentos formatada com Bootstrap.
O mesmo tipo de alteração deverá ser efetuado no template funclist.
python
O resultado das alterações efetuadas pode ser observado na imagem seguinte, obtida durante a execução do
sistema:
Funcionários
Novo Funcionário
Matricula Nome Cadastro DeptoOpções
Excluir
Alterar
Listagem de funcionários formatada com Bootstrap.
Templates de cadastros
Podemos modificar os formulários do sistema, a começar pelo template deptodata.
python
Dados do Departamento
Nome
Cadastrar
As alterações efetuadas foram mínimas, incluindo o encapsulamento do conteúdo original em uma div com a
classe container, e o uso das classes btn e btn-primary no botão de envio. Quanto aos componentes de entrada,
cada conjunto formado por label e input é colocado em uma div do tipo form-group, além do uso da classe form-
control no input e form-label no label, o que traz, além de boa organização, alguns efeitos visuais no ganho de
foco.
O resultado das mudanças pode ser observado a seguir:
Formulário de departamento formatado com Bootstrap.
Alterações semelhantes precisam ser efetuadas no template funcdata.
python
Dados do Funcionário
Matricula
Nome
Departamento
Cadastrar
A presença de uma caixa de seleção levou à adoção da classe form-select. Vejamos o resultado das
modificações:
Formulário de funcionário formatado com Bootstrap.
Tratamento de erros
Em muitas situações, você deve ter tentado excluir um departamento e ocorreu um erro, o que normalmente é
causado pela quebra da integridade referencial, imposta pelo relacionamento de um para muitos. É interessante
informar o erro ao usuário de forma adequada, utilizando mensagens claras. Faremos isso aproveitando nossa
tela inicial, no template home, de acordo com a listagem seguinte:
python
Tudo que fizemos foi acrescentar uma div com a classe container, no primeiro nível, e uma div interna com as
classes alert e alert-danger, que será responsável por emitir um alerta, na tela principal, com a mensagem de
erro. O texto da mensagem é preenchido pelo atributo th:text, mas será exibido apenas se um objeto de
mensagem for enviado a partir do controlador, com base na condição utilizada no atributo th:if.
Precisamos alterar o método excluir, em DepartamentoController, para o tratamento de erros na utilização da
rota /departamentos/excluir.
java
Execute novamente o aplicativo e tente remover um departamento que tenha funcionários, o que fará exibir a
mensagem enviada a partir do tratamento da exceção pelo controlador, como observado a seguir:
Mensagem de erro exibida na tela principal.
Acréscimo de autenticação no aplicativo
Configuração da autenticação
Nosso aplicativo está totalmente funcional, mas seria interessante acrescentar algum modelo de autenticação.
Não trabalharemos com um padrão robusto, mas apenas com um exemplo simples, adotando usuário fixo no
controlador de autenticação, o que não impedirá a utilização de todo o fluxo de execução, com os componentes
associados e a integração com Thymeleaf.
Inicialmente, vamos acrescentar a dependência para o Spring Security no arquivo pom.xml.
@GetMapping("/departamentos/excluir")
public String excluir(Model model, Integer idDepto) {
try {
repositorio.deleteById(idDepto);
}catch(Exception ex){
model.addAttribute("mensagem",
"Erro ao remover departamento. "+
"Verifique se não há funcionários associados.");
return "home";
}
model.addAttribute("departamentos", repositorio.findAll());
return "deptolist";
}
python
Tentando executar novamente o aplicativo, será exibida a tela de login, impedindo a utilização do sistema, pois a
autenticação não está configurada. Precisamos configurá-la criando a classe AuthController, no pacote com.univ
ersidade.
java
org.springframework.boot
spring-boot-starter-security
org.thymeleaf.extras
thymeleaf-extras-springsecurity5
@EnableWebSecurity
@Configuration
public class AuthController extends WebSecurityConfigurerAdapter
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user = User.builder().username("usu1")
.password(passwordEncoder().encode("1234"))
.roles("USER").build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.and().logout().logoutSuccessUrl("/");;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/webjars/**");
}
}
As anotações Configuration e EnableWebSecurity integram nossa classe ao contexto do Spring, definindo-a
como controladora de segurança-padrão. Ainda em termos da integração, temos dois beans: um para gerência
da criptografia (passwordEncoder), que utiliza algoritmo BCrypt, e outro para a base de usuários (userDetailsSer
vice), em que é criado apenas um usuário, com login usu1 e senha 1234, o qual é anexado ao gestor em memória.
Atenção
No método configure, tendo como parâmetro um objeto do tipo HttpSecurity, efetuamos todo o
controle de autenticação, com bloqueio por padrão, mas liberando as rotas para a raiz e para o login.
Outro elemento configurado é o modelo utilizado para o login, que, no caso, será feito a partir de um
formulário (formLogin), além do redirecionamento do logout para a página inicial, por meio de
logoutSuccessUrl, impedindo que seja solicitado um novo login de imediato.
O segundo método configure, com parâmetro do tipo WebSecurity, é necessário para garantir a funcionalidade
do Bootstrap, mesmo que o usuário não esteja autenticado. Para tal, usamos a diretiva ignoring, aplicada a toda
rota partindo de webjars.
Já podemos executar nosso projeto e verificar como o login é solicitado quando acessamos o cadastro de
departamentos ou o de funcionários. Nosso próximo passo será implementar o logout, com a modificação do
template menu, apenas no trecho em que encontramos o botão de saída do sistema, conforme o fragmento de
código apresentado a seguir:
python
Trocamos o botão por um link, apontando para a rota logout, e permitimos sua visualização apenas quando há
um usuário logado, o que é feito com o teste isAuthenticated, no atributo sec:authorize. Apenas com a utilização
dos extras do Thymeleaf para integração com Spring Security, incluídos nas dependências do projeto, foi
possível utilizar o dialeto específico voltado para a segurança.
Logout
Personalização das telas de login e logout
Como passo final, vamos personalizar a tela de login para que fique integrada ao ambiente de nosso sistema.
Começamos com a criação de um template com o nome login.html.
python
Efetue o Login
Usuário ou senha inválido(s).
Por se tratar de uma personalização, devemos manter a chamada do formulário e o nome de cada campo. No
caso, os campos são username e password, com envio de informação para /login, no modo POST. Temos ainda
uma div configurada como alert-danger para avisar que os valores utilizados são inválidos, a qual é ativada
apenas quando existe param.error fornecido pelo controlador do Spring Security.
Todas as demais configurações utilizadas são semelhantes às que foram adotadas nas demais páginas do
sistema. Podemos observar a aparência da nova tela de login a seguir:
Login personalizado no sistema.
Precisamos também de um template com o nome logout.html para a confirmação da saída.
pythonLogout do Usuário
Clique no botão para confirmar o logout
Novamente, temos que nos integrar ao comportamento do framework. Tudo de que precisamos é uma chamada
para a rota /logout em modo POST. Nossa janela apenas apresenta um título, mensagem para o usuário, e botão
do tipo btn-danger, para efetivar o logout, como podemos observar a seguir:
Confirmação de logout personalizada no sistema.
Para que as personalizações sejam efetivadas, precisamos adicionar dois mapeamentos em modo GET na classe
HomeController.
java
As rotas chamadas no modo GET direcionarão para nossos templates, enquanto o modo POST é interceptado
pelo Spring Security, mantendo todo o arcabouço de autenticação.
Precisamos alterar o método configure, com parâmetro do tipo HttpSecurity, em nossa classe AuthController,
responsável pela autenticação dos usuários, de acordo com o fragmento de código apresentado a seguir. Note
que incluímos apenas os elementos necessários para indicar as rotas para o login (loginPage) e para o logout (log
outUrl):
java
Ao trabalhar com modelos mais robustos de autenticação, podem ser necessárias alterações no arquivo applicati
on.properties, bem como no acesso a bancos de dados externos.
@GetMapping("/login")
public String showLogin(Model model) {
return "login";
}
@GetMapping("/logout")
public String showLogout(Model model) {
return "logout";
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login")
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/");
}
Teoria na prática
Construção de Menus com Bootstrap.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Vem que eu te explico!
Os vídeos a seguir abordam os assuntos mais relevantes do conteúdo que você acabou de estudar.
Fundamentos do Bootstrap
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Personalizando o Spring Security
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Verificando o aprendizado
Questão 1
O framework Bootstrap viabiliza a construção de interfaces responsivas, alinhadas com os melhores padrões de
design, de forma extremamente simples, com base em classes CSS, o que permite, inclusive, a escolha de
temas personalizados para cada empresa. Por exemplo, se nosso sistema pergunta, em uma caixa de diálogo,
se desejamos excluir todos os registros, qual seria a classe CSS correta para o botão de confirmação?
A
btn-primary
B
btn-success
C
btn-info
D
btn-danger
E
btn-link
A alternativa D está correta.
As classes específicas para botões apresentam cores e formatos padronizados para cada ambiente de
execução quando adotamos o Bootstrap. Quanto ao exemplo de nosso sistema, a exclusão de todos os
registros de uma base é sempre uma operação perigosa, que exige plena atenção do usuário na
confirmação, o que levará à adoção da classe btn-danger, como em
Confirmar , causando a utilização da cor vermelha, normalmente associada ao perigo.
Questão 2
Uma caraterística muito interessante do Thymeleaf é sua fácil integração com o modelo de segurança do
Spring, com base no Spring Security, o que permite a definição de templates com o controle da disponibilidade
de elementos para perfis específicos de usuários, ou simplesmente a alternância da visibilidade de algum trecho
quando ocorre autenticação. Para o segundo caso, qual método retorna ao estado atual da autenticação?
A
isLogged
B
isAuthorized
C
isAuthenticated
D
isCertified
E
isOpened
A alternativa C está correta.
Para integrar o Thymeleaf com Spring Security precisamos apenas acrescentar a dependência thymeleaf-
extras-springsecurity na versão utilizada pelo projeto. Com o acréscimo dessa dependência, o prefixo sec
(Security) fica disponível para uso nos atributos das tags, com acesso aos mecanismos de segurança do
ambiente.
O método isAuthenticated retorna true para um usuário autenticado. Logo, a visibilidade do componente
pode ser alternada facilmente, com o acréscimo do atributo sec:authorize="isAuthenticated()".
4. Conclusão
Considerações finais
De forma geral, pessoas têm competências diferentes. Muitos programadores não apresentam habilidade para a
construção de interfaces de usuário complexas, algo que é uma característica dos designers. Entretanto, com a
alta padronização das interfaces atuais, surgiram ferramentas que viabilizam a construção de interfaces de alta
qualidade com baixo esforço.
Mesmo com a disponibilidade dessas ferramentas, o conhecimento dos princípios elementares para a
construção de páginas é necessário, principalmente para sua organização estrutural. É preciso conhecer
sintaxes como HTML, Java Script e CSS, além de frameworks comuns no mercado, como JQuery UI.
Após a compreensão dos princípios para a construção das páginas, precisamos integrá-las com os
componentes da camada Controller, dentro de uma arquitetura MVC. O uso de Thymeleaf nos proporcionou essa
integração de forma natural, com base em templates HTML, que podem ser manipulados por designers, por
meio de ferramentas de edição apropriadas.
Com as interfaces integradas, os sistemas garantem as funcionalidades definidas nos requisitos, mas o mercado
exige características como responsividade e padronização no design das páginas. O Bootstrap permite o
acréscimo dessas características de forma não invasiva, por meio de classes para estilização, garantindo
qualidade e produtividade necessárias.
Podcast
Para encerrar, ouça sobre processo de criação para páginas HTML, bem como a integração com a
camada de controle de um sistema Spring Web por meio do Thymeleaf.
Conteúdo interativo
Acesse a versão digital para ouvir o áudio.
Explore +
Acesse o guia CSS Tutorial, disponibilizado pela W3Schools, e navegue pelas opções do menu, no lado esquerdo
da página, para aprender tudo que é necessário sobre CSS.
Explore os diversos exemplos disponibilizados pelo JQuery UI (jqueryui.com), navegando pelas opções do menu
lateral esquerdo e utilizando a opção "view source" para conferir os códigos.
Confira o guia Bootstrap 5 Tutorial - An Ultimate Guide for Beginners e navegue pelas opções do menu lateral
esquerdo para aprender a utilizar o Bootstrap com base em exemplos.
Leia o artigo Working with Fragments in Thymeleaf, oferecido pelo Baeldung, e compreenda melhor a utilização
de fragmentos no Thymeleaf.
Leia o artigo Use Thymeleaf Templates with Spring WebFlux to Secure Your Apps, de Jimena Garbarino,
descrevendo a autenticação em arquitetura de fluxo com Spring e Thymeleaf.
Referências
BOAGLIO, F. SpringBoot. 1. ed. São Paulo: Casa do Código, 2017.
BURKE, B; MONSON, R. Enterprise Java Beans 3.0. 5. ed. São Paulo: Pearson, 2007.
DEBLAUWE, W. Taming Thymeleaf. 1. ed. USA: Lulu Press, 2021.
DEITEL, H; DEITEL, P. Java, Como Programar. 10. ed. São Paulo: Pearson, 2016.
HORSTMANN, C. Modern Java Script For The Impatient. 1. ed. USA: Addison-Wesley, 2020.
MAZZA, L. HTML5 e CSS3. 1. ed. São Paulo: Casa do Código, 2014.
PATEL, N. Spring 5.0 Projects. 1. ed. Reino Unido: Packt Publishing, 2019.
SOUZA, N. Bootstrap 4. 1. ed. São Paulo: Casa do Código, 2018.
SPILCA, L. Spring Security In Action. 1. ed. USA: Manning, 2020.
TURNQUIST, G. Learning Spring Boot 2.0. 2. ed. Reino Unido: Packt Publishing, 2017.
ZAKAS, N. High Performance Java Script. 1. ed. USA: O'Reilly, 2010.
Camada de visão
1. Itens iniciais
Propósito
Preparação
Objetivos
Introdução
1. Implementação de uma página simples da web
HTML 5
Sintaxe
Tags básicas
Primeira página
UNIVERSIDADE ©2022
Tabelas
Formulários
MathML
Âncoras
CSS
Folhas de estilo
Qualificador hover
Exemplos práticos
Exemplo 1
Exemplo 2
Java Script
Operadores do Java Script
Estrutura condicional
Biblioteca Java Script
Fatoriais de 3 a 8
Primos entre 50 e 75
Tratamento de eventos
Notação JSON
JQuery
Aplicação básica
Diálogos modais e respostas a eventos
Tarefas
Comentário
Chamadas assíncronas
Receitas para Drinks
Comentário
Teoria na prática
Conteúdo interativo
Vem que eu te explico!
Sintaxe HTML
Conteúdo interativo
Formatação com CSS
Conteúdo interativo
Verificando o aprendizado
2. CRUD utilizando o framework Thymeleaf
Framework Thymeleaf
Camada model
Dica
Gerenciador para os modelos do Thymeleaf
Comentário
Camada Controller
Camada View
Comentário
Filtro de interceptação
Comentário
Spring Boot com Thymeleaf
Criação do projeto
Controlador Spring Web
Configuração dos templates
Departamentos:
Definição de mensagens
Departamentos
Persistência e controle
Camada de persistência
Camada de controle
Comentário
Interface do CRUD
Templates para departamentos
Departamentos
Dados do Departamento
Templates para funcionários
Funcionários
Dados do Funcionário
Teoria na prática
Conteúdo interativo
Vem que eu te explico!
Características do Thymeleaf
Conteúdo interativo
Filtro de Interceptação
Conteúdo interativo
Verificando o aprendizado
3. Estudo de caso com Framework Bootstrap
Framework do Bootstrap
Estilos de botões
Estilos de Botões
Caixas de diálogo modais
Exemplo de Modal
Comentário
Sistema de Grid
Componentes para navegação
Uso de Bootstrap com Thymeleaf
Definição do menu
Navegação utilizando o menu
Alteração das interfaces do CRUD
Templates com listas
Departamentos
Funcionários
Templates de cadastros
Dados do Departamento
Dados do Funcionário
Tratamento de erros
Acréscimo de autenticação no aplicativo
Configuração da autenticação
Atenção
Personalização das telas de login e logout
Efetue o Login
Logout do Usuário
Clique no botão para confirmar o logout
Teoria na prática
Conteúdo interativo
Vem que eu te explico!
Fundamentos do Bootstrap
Conteúdo interativo
Personalizando o Spring Security
Conteúdo interativo
Verificando o aprendizado
4. Conclusão
Considerações finais
Podcast
Conteúdo interativo
Explore +
ReferênciasPOST
do protocolo HTTP. Após o preenchimento dos dados, o clique no componente do tipo submit, ou botão de
envio, tentará enviar os dados em background para o endereço, o que causará um erro de acesso, mas, no caso
de uma rota real, estaria enviando esses dados para um componente do servidor capaz de tratá-los, como um
Servlet, por exemplo.
O id deve ser utilizado nos relacionamentos internos da página, enquanto name define a variável que será
enviada para o servidor. O value define o valor que será enviado para o servidor, associado à variável definida via
name, devendo ser fixado para todos os componentes que não permitem edição de valor via teclado.
A lista de valores simples datalist deve ser associada ao componente do tipo input por meio do atributo list. Note
o uso de id para o relacionamento, da mesma forma que nas tags do tipo label, para associação com os
componentes corretos.
O metadado viewport, de grande utilidade na garantia da responsividade, configura a página para se adequar ao
espaço de visualização oferecido pelo dispositivo.
Executando a página no navegador, teremos a tela de entrada.
Nome Completo
Nome:
Sobrenome:
Declaro aceitar todos os termos de uso do aplicativo.
Cobrança:
Formulário Exemplo003.html
MathML
O MathML é uma linguagem de marcação para exibir e capturar estruturas e conteúdo matemático. Vejamos um
Exemplo004.html.
python
A sintaxe MathML é bastante complexa e extensa, mas a listagem nos traz muitos elementos de utilização
comum. Inicialmente, definimos uma linha principal com mrow, e na sequência, temos a integral e seus limites,
dentro de msubsup, com o símbolo em mo, limite inferior em mn e limite superior em mi.
O conteúdo interno da integral será uma fração, com mfrac, em que a parte superior deve ficar em uma tag
mrow, a qual envolve, no exemplo, um logaritmo na base 3, definido com msub, com a função em mi e a base em
∫ 1 t
log 3
x
x
ⅆ x
mn, seguido da variável x na tag mi, enquanto a parte inferior tem apenas a variável x em uma tag mi. Na última
parte, temos o sinal diferencial, em uma tag mo, seguido da variável para integração x na tag mi.
Toda a expressão matemática deve ser encapsulada em uma tag math, em que devemos estar atentos para a
utilização do namespace adequado. Para testar, devemos utilizar o Firefox, pois nem todos os navegadores
oferecem suporte direto ao MathML, sendo necessário adicionar plugins a navegadores como Chrome e Edge.
Apresentação do Exemplo004.html, via Firefox.
Âncoras
As âncoras, com base na tag a, definem a navegação entre páginas e conteúdo, justificando a classificação dos
documentos como hipertexto. Uma âncora pode definir um local dentro de uma página extensa, navegar para o
trecho nomeado, ou definir um link para algum endereço. É bastante comum sua utilização junto a imagens,
incorporadas às páginas por meio da tag img.
Vejamos o Exemplo005.html.
python
Como um dos objetivos é demonstrar a navegação interna da página, para situações em que o conteúdo é muito
extenso, foram adicionadas muitas quebras de linha. Para marcar o local da página, utilizamos o atributo id da
âncora, enquanto a navegação para o trecho ocorre com o uso do valor de id, precedido por cerquilha, no
atributo href.
A navegação para outros sites ou páginas também é definida no atributo href, com o descritivo da âncora
colocado entre a tag e seu fechamento. No lugar do texto descritivo, podemos usar uma imagem (img), definindo
um ícone clicável.
O resultado da execução da página, que receberá o nome Exemplo005.html, é apresentado a seguir. Como temos
uma página extensa, foi dividida em duas partes, em que o clique sobre a âncora com texto "própria" levará para
o final dela.
Podemos utilizar âncoras para navegar até algum site, como o
Spring IO,
ou para um local da própria página.
Link para exemplo001
Aqui fica o local marcado.
https://spring.io/projects
exemplo001.html
exemplo001.html
exemplo001.html
exemplo001.html
exemplo001.html
exemplo001.html
Visualização do Exemplo005.html.
CSS
Folhas de estilo
Com o papel estrutural que o HTML assumiu, vários dos atributos de configuração tipográfica das versões
antigas se tornaram obsoletos, deslocando-se a responsabilidade para as folhas de estilo, ou CSS (Cascade
Style Sheets).
As folhas de estilo podem ser adicionadas na tag do HTML (inline), em um trecho delimitado por style, ou em um
arquivo externo. Em termos de reutilização e responsividade, a melhor opção é o uso de arquivo externo, pois
permite centralizar as formatações e versionar para cada plataforma, ou seja, utilizar pontos de quebra para
formatações específicas.
Vamos voltar ao primeiro exemplo de HTML e aplicar algumas formatações sobre as tags, adotando um setor
delimitado por style.
python
Aqui definimos elementos comuns para algumas tags visuais, como cor da fonte (color), cor do fundo
(background-color) e alinhamento do texto (text-align). Também temos uma borda do tipo tracejada sendo
aplicada ao redor das listas.
Podemos aplicar as formatações a várias tags simultaneamente, desde que as separemos por vírgula, e qualquer
atributo definido será combinado com as formatações já existentes. Aliás, as folhas de estilo são denominadas
como "em cascata" porque podem ser sobrepostas.
Qualificador hover
O qualificador hover permite definir uma formatação para o momento em que o mouse repousa sobre o
componente. No exemplo, a página tem fundo preto e fonte branca como padrão, mas, quando o mouse passa
sobre um parágrafo, ele muda a cor de fundo para marrom.
Formatações aplicadas às tags do arquivo Exemplo001.html.
Para formatar um elemento específico da página, o seletor correto seria um identificador, ou id, em que uma
utilização comum é na criação de áreas para organização do conteúdo. Como o padrão tableless desencoraja o
uso de tabelas com esse fim, o uso de áreas identificadas, com base em tags do tipo div, formatadas via CSS,
transformou-se na estratégia preferencial.
Identificadores são voltados para a individualização de algum componente na página, sendo definidos na folha
de estilo com um nome precedido de cerquilha. Para que a formatação seja utilizada, devemos ter um
componente com atributo id utilizando o mesmo nome.
Exemplos práticos
Exemplo 1
A seguir, um arquivo de folha de estilos com o nome Exemplo006.css.
python
De acordo com a formatação adotada, toda tag div definirá uma área com largura (width) de 200 pixels e altura
(height) de 150 pixels, utilizando texto alinhado ao centro e tamanho de fonte ajustado para 40 pixels. Em
seguida, temos a individualização das características para cada uma das áreas que será criada. A combinação
das formatações da div e do identificador definirão o posicionamento e as dimensões dessas áreas.
Nas formatações específicas, temos a definição da cor de fundo (background-color), estilo da borda (boder-
style), e posicionamento absoluto, em relação ao limite superior esquerdo da página, considerado como origem
dos eixos horizontal e vertical. Com os valores utilizados, teremos uma área a 10 pixels do topo e 10 pixels da
esquerda, e outras duas áreas utilizando os valores 110 e 210, respectivamente.
A página Exemplo006.html, com a listagem a seguir, utiliza a folha de estilos definida anteriormente.
Div {text-align: center; font-size:40px; width:200px; height:150px}
#camada1 {background-color: yellow; border-style: dotted;
position: absolute; top:10px; left:10px}
#camada2 {background-color: range; border-style: dashed;
position: absolute; top:110px; left:110px}
#camada3 {background-color: gold; border-style: solid;
position:absolute; top:210px; left:210px}
python
A página é bastante simples, com a definição das áreas por meio de tags do tipo div, relacionadas aos estilos
específicos por meio do atributo id. Devemos lembrar que teremos a combinação das formatações para a tag e
para o identificador em cada área.
O relacionamento entre os arquivos HTML e CSS foi definido por meio da tag link, em que temos o atributo rel
com o valor stylesheet (folha de estilos), e o nome do arquivo em href. Vejamos o resultado da interface:
Exemplo de CSS.
Ao contrário dos identificadores, que definem formatações para elementos específicos, as classes, definidas
com a utilização de ponto antes do nome, servem para formatar elementos que se repetem ao longo da página.
Exemplo 2
Primeira Camada
Segunda Camada
Terceira Camada
Vamos criar a página Exemplo007.html, contendo a listagem a seguir.
python
O que temos no código é a definição de uma lista principal, em que os itens são links para outras páginas e
endereços, além de um item contendo outra lista, o que seria um submenu, com mais alguns links. A relação com
a folha de estilos ocorre por meio da tag link, na divisão head, a qual não terá efeito antes da criação do arquivo.
Inicio
Exemplos...
Primeiro
Segundo
Pesquisa
•
•
◦
◦
•
exemplo007.html
#
exemplo001.html
exemplo002.html
https://www.google.com
Página com menu, sem a folha de estilos.
Como podemos observar, temos a exibição das listas, contendo as âncoras, em que os links estão funcionais,
mas ainda sem a formatação adequada. Veremos que as folhas de estilos efetuam modificações extremas na
forma de apresentação, permitindo que o HTML assuma apenas o papel estrutural, de acordo com a divisão de
responsabilidades exigida pela W3C.
Vamos estilizar nosso menu por meio da criação do arquivo Exemplo007.css, contendo a listagem a seguir.
python
Se voltarmos ao nosso código HTML, veremos que a lista principal, na primeira tag ul, tem o valor menu no
atributo class, definindo a utilização das formatações associadas à classe menu, da folha de estilos. Da mesma
forma, a lista interna é atribuída à classe submenu, que também será especificada na folha de estilos.
Começamos com uma formatação geral, por meio do asterisco, para a remoção das margens da página, além da
definição da fonte com 12 pixels, na tag body. Nos passos seguintes, temos as classes menu e submenu sendo
configuradas.
A classe menu remove o marcador dos itens de lista para todos os elementos internos, com base no
atributo list-style, além de usar cor de fundo amarela e posicionamento relativo à esquerda da página,
por meio do valor left no atributo float, para cada item. Para o submenu, temos posicionamento de 25
pixels em relação ao topo do contêiner atual, no caso o item do menu, e fundo laranja, não sendo
visível inicialmente, devido ao uso de none para display.
*{margin: 0; padding: 0}
body{font-size: 12px}
.menu{list-style:none; background-color: yellow; float:left}
.menu li{position:relative; float:left;
border-right:1px dashed black}
.menu li a{color:black; text-decoration:none;
padding:5px 10px; display:block}
.menu li a:hover{background:black; color:white}
.submenu{position:absolute; top:25px; left:0;
background-color:orange; display:none}
.submenu li{border-bottom: 1px dashed black;
display:block; width:120px}
.menu li:hover ul, .menu li.over ul{display:block}
As demais formatações apresentam duas características interessantes na criação de seletores: o agrupamento e
a dependência. Podemos agrupar seletores separados por vírgula, em que a mesma formatação é aplicada a
todos, ou especificar uma dependência por meio do uso de espaço, em que o estilo é aplicado apenas quando a
hierarquia é observada.
Segundo as características definidas, uma tag li, dentro de um elemento menu, respeitará uma sequência de
posicionamento a partir da esquerda, além de apresentar uma borda tracejada de 1 pixel à direita (border-right).
Da mesma forma, na formatação seguinte, temos as âncoras dentro das tags li, a partir de menu, com remoção
do sublinhado, por meio de text-decoration, definição do espaçamento em padding, e visualização (display)
como bloco, em que temos a área preenchida, sem outros elementos HTML ao redor, a não ser que sejam
configurados com valor left em float.
Ainda com relação às âncoras, o qualificador hover fará com que seja utilizado fundo preto e fonte
branca quando o mouse estiver sobre elas.
Temos ainda a formatação para a tag li, a partir de submenu, com definição da borda inferior, visualização em
bloco e largura de 120 pixels. Como o submenu estará dentro de menu, temos formatações que se propagam
para li, como a borda direita, ao mesmo tempo em que, devido à precedência da visualização do contêiner, as
tags li de submenu ficarão invisíveis no início.
Para que o submenu fique visível, temos uma formatação muito específica, em que o qualificador hover é
aplicado uma tag li de menu sequenciada por ul, mudando o valor do atributo display da ul para block enquanto o
mouse estiver sobre o item. Vejamos a interface resultante:
Página com menu, utilizando a folha de estilos.
Java Script
O dinamismo das páginas exige a utilização de uma linguagem de programação adequada, no caso o Java Script,
e interação baseada na utilização de eventos.
Imagem ilustrativa JavaScript.
Utilizamos o Java Script para controle de elementos da página
HTML e sua interatividade, em que o código pode ser incluído
na própria página, ou organizada no formato de biblioteca, como
um arquivo externo. Caracterizada originalmente como uma
linguagem para o navegador, hoje temos muitas tecnologias que
adotam Java Script no lado servidor, como NodeJS.
Uma variável pode ser declarada com o uso de var, ou
simplesmente por meio da inicialização. Como o Java Script não
é fortemente tipado, a variável assume o tipo do valor associado
a ela. Os tipos com os quais a linguagem trabalha são: numérico,
booleano, texto, objeto e vetor, este último como objeto da classe Array.
Operadores do Java Script
Vamos começar pela criação da página Exemplo008.html.
python
No exemplo, são declaradas três variáveis, com a utilização de var, sendo efetuadas algumas operações
aritméticas sobre elas. A cada passo, é impresso na página o valor das variáveis, dentro de tags do tipo li, por
meio de document.writeln, com a concatenação dos valores no texto efetuada pelo sinal de adição.
Com relação aos operadores do Java Script, podem ser observados na tabela seguinte:
Operador Tipo Utilização
+ += ++ Aritmético Operação de adição. Quando colocado à esquerda
da igualdade, o valor é somado à variável
receptora. Duplo mais incrementa um. Também é
utilizado na concatenação de texto.
- -= -- Aritmético Operação de subtração. Quando posicionado à
esquerda, temos a subtração do valor no receptor.
Duplo menos decrementa um.
Operador Tipo Utilização
* *= Aritmético Multiplicação de valores. Quando posicionado à
esquerda, temos a multiplicação do receptor pelo
valor.
/ /= Aritmético Operação de divisão. Quando posicionado à
esquerda, temos a divisão do receptor pelo valor.
% %= Aritmético Resto da divisão inteira. Quando posicionado à
esquerda, obtemos o resto da divisão do receptor
pelo valor.
> >= Relacional Comparação entre valores, dos tipos maior e maior
ou igual, com resultado do tipo booleano.
Lógico Operação de negação. Inverte o resultado da
operação lógica.
Tabela: Operadores oferecidos na sintaxe do Java Script.
Denis Cople.
A divisão inteira trunca o valor no Java, mas não no Java Script, pois não temos como diferenciar entre inteiro e
ponto flutuante. Outra diferença para o Java é a comparação de texto: no Java, é necessário o método equals,
enquanto o Java Script adota o operador de igualdade padrão.
Vejamos a interface gerada:
Utilização de Java Script em Exemplo008.html.
Estrutura condicional
A similaridade entre Java Script e derivados do C também ocorre na sintaxe para as estruturas de decisão e de
repetição. Vejamos um exemplo de estrutura condicional, no arquivo Exemplo009.html.
python
A função prompt serve para solicitar ao usuário a digitação de um valor a partir de um diálogo de entrada,
enquanto a função eval efetua a conversão do texto para número. De acordo com nosso exemplo, a variável x
recebe o valor digitado pelo usuário, com o uso da função prompt, ocorrendo a conversão para numérico por
meio de eval.
Com base no valor digitado, a página apresentará uma das alternativas de frase, sempre com o encapsulamento
na tag h1, ocorrendo a escolha da frase pela estrutura condicional if..else, segundo a condição x > 5. Vejamos o
resultado final:
Execução da página Exemplo009.html, com estrutura condicional.
As funções, bem como os métodos da orientação a objetos, são processos nomeados, que podem ou não
apresentar parâmetros e retornar valores. Criamos funções no Java Script com o uso de function, e elas podem
retornar valores para o chamador por meio de return.
Biblioteca Java Script
Vamos começar com a definição do arquivo ExemploLib.js, em que teremos uma biblioteca Java Script.
javascript
Nessa biblioteca, temos duas funções: cálculo do fatorial e a verificação se o número fornecido é primo. Em
ambos os casos, temos um parâmetro de entrada x e uso de return para fornecimento do resultado, embora não
sejam elementos obrigatórios.
A função fatorial é recursiva, com base em uma condicional, em que valores menores do que 2
causam o retorno de 1, enquanto outros especificam o retorno a partir da multiplicação do valor pelo
fatorial do anterior.
Já para o teste de primalidade, ele foi codificado com base em uma estrutura de repetição, em que ocorre o
retorno de false quando o parâmetro é divisível por algum valor do loop, segundo o teste do resto da divisão, ou t
rue, após o loop, quando não é divisível por nenhum dos valores, o que é justificado pela regra de que o número
primo só pode ser dividido por 1 ou por ele mesmo.
Com nossa biblioteca Java Script pronta, vamos criar o arquivo Exemplo010.html.
function fatorial(x) {
if (xDiálogos modais e respostas a eventos
Vamos verificar a construção de diálogos modais e implementação da resposta a eventos, criando o arquivo Exe
mplo013.html.
Data:
python
No exemplo, temos uma página para definição de listas de tarefas, com a inclusão de tarefas na lista a partir de
um diálogo modal.
Vamos analisar o diálogo modal, que é construído sobre um elemento do tipo div, com o título "Definição de
Tarefas", contendo um formulário em seu interior. Para transformar o componente em um diálogo modal,
invocamos o modificador dialog, tendo como atributos: altura (height) de 200 pixels, largura (width) de 350
pixels, abertura automática (autoOpen) desativada e comportamento modal ativado.
O primeiro botão invocará a função criarTarefa, responsável pela adição de uma tarefa à lista, e o segundo
apenas provocará o fechamento do diálogo a partir de uma função anônima. Temos ainda o evento de
fechamento do diálogo (close), que limpa o campo de texto (edtTarefa) quando o diálogo é fechado, fornecendo
um texto vazio para val.
Tarefas
Tarefa
Abrir Dialogo
Comentário
A criação da tarefa pode ser observada na função criarTarefa, em que temos a inclusão de um
elemento do tipo li à lista, identificada como listaTarefas, por meio do método append. Além da
utilização de uma classe CSS e de um estilo complementar para definição do espaçamento,
modificando a aparência padrão do item de lista, temos a captura do valor que foi digitado no campo
de texto, por meio da val, para utilização no item. Note que o fechamento do diálogo é invocado após
o acréscimo da tarefa.
Segundo o comportamento padrão de um formulário, ocorreria o envio da informação com o pressionamento da
tecla "Enter". Para evitar que ocorra uma mudança de endereço, limpando os dados já inseridos, buscamos (find)
o form a partir de dlgTarefa, e associamos o evento de submit, em que eliminamos o comportamento padrão (pre
ventDefault) e fechamos o diálogo.
Note o uso do método on, que associa um evento de determinado tipo a uma função para o tratamento. A
mesma técnica foi adotada para a resposta ao clique no botão btnDialog, que efetua a abertura do diálogo com
a chamada do tipo open.
Para a lista, foi utilizado o modificador sortable, que permite arrastar os itens adicionados à lista, modificando
sua ordem de apresentação.
A interface criada pode ser observada a seguir:
Diálogo e lista ordenável, arquivo Exemplo013.html.
Chamadas assíncronas
Algo que se tornou muito comum nos sites atuais foi a utilização de chamadas assíncronas, segundo um
compêndio de tecnologias conhecido como AJAX. A biblioteca JQuery fornece um método muito simples para
efetuar chamadas desse tipo. Como o Java Script permite lidar com JSON de forma nativa, o tratamento da
resposta para Web Services RESTful não oferece qualquer tipo de dificuldade.
Para demonstrar o uso de AJAX, vamos criar o arquivo denominado Exemplo014.html.
python
Em termos de componentes HTML relevantes, temos apenas uma caixa de texto (edtNome), um botão
(btnEnviar) e uma divisão (listaDrinks). A divisão merece atenção especial, pois serve de base para um
componente accordion, o qual fornece a criação de áreas retráteis agrupadas, em que cada área receberá um tít
ulo, por meio da tag h3, e o conteúdo em uma div.
Temos a associação do clique ao botão, com uma função de tratamento que limpa o conteúdo interno de listaDri
nks, eliminando os resultados de consultas anteriores, formando o endereço de pesquisa, concatenando o valor
digitado em edtNome, e efetuando a chamada AJAX.
Como temos uma chamada assíncrona, é fornecido um objeto com o endereço (url) e a manipulação para um
retorno bem-sucedido (success), em que invocamos tratarDados com a passagem do retorno.
Receitas para Drinks
Pesquisar
Comentário
O endereço é um serviço RESTful internacional para a consulta de receitas de drinks, que retorna
diversas informações no formato JSON. Como cada drink normalmente apresenta variações, teremos
um vetor de registros em que resgataremos os campos strDrink, strDrinkThumb e strInstructions,
representando, respectivamente, o nome, a foto e a receita do drink. Lembre-se de que as receitas
estarão em inglês, mas a tradução da página, por meio do navegador, apresentou ótimos resultados
nos testes efetuados.
A função tratarDados recebe a resposta no formato JSON, e utiliza o operador forEach sobre o vetor drinks,
invocando adicionaLinha a cada registro, com a atualização do accordion, ao final, por meio da opção refresh.
O formato de adicionaLinha é padronizado para o uso de forEach, com os parâmetros representando o registro e
o índice no vetor, em que item servirá de base para construir uma área de nosso accordion, com o título (h3)
associado a strDrink e o conteúdo (div) definido a partir de uma imagem apontando para strDrinkThumb, ao lado
do texto referente às instruções da receita (strInstructions). Vejamos o resultado:
Utilização de AJAX.
Existem muitos outros componentes disponíveis na biblioteca JQuery UI, inclusive opções para a criação de
menus, barras de progresso e abas de navegação.
Teoria na prática
Construção de páginas com JQuery UI.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Vem que eu te explico!
Os vídeos a seguir abordam os assuntos mais relevantes do conteúdo que você acabou de estudar.
Sintaxe HTML
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Formatação com CSS
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Verificando o aprendizado
Questão 1
Segundo as diretivas da W3C, enquanto o HTML fica com a responsabilidade de estruturar as páginas, toda
formatação visual deve ser implementada por meio de folhas de estilo. Mais do que formatações, o CSS permite
controlar alguns comportamentos, como a mudança do aspecto do componente com a passagem do mouse
sobre ele, o que é feito com o qualificador
A
empty.
B
visited.
C
focus.
D
hover.
E
active.
A alternativa D está correta.
Com o CSS, aplicamos formatações a elementos específicos da página, por meio de seletores, além de
permitir a definição de alguns comportamentos via qualificadores. Por exemplo, empty define a aparência
quando o componente não tem filhos, focus para aquele componente que detém o foco, visited é utilizado
nos links já visitados, active para o momento em que ocorre o clique, e hover para a passagem do mouse
sobre o componente.
Questão 2
O surgimento do JQuery, um framework construído totalmente em Java Script, trouxe novas possibilidades para
a criação de páginas para Web. Com uma sintaxe que alia os seletores CSS às melhores práticas de
programação em Java Script, permitiu a realização de tarefas que antes eram consideradas trabalhosas com
poucas linhas de código, além de oferecer diversos módulos que expandem sua funcionalidade básica, como
JQuery UI. Qual métododo JQuery UI deve ser utilizado para criar uma sequência de painéis, cada um com
título e possibilidade de ocultação?
A
dialog
B
slider
C
accordion
D
tooltip
E
spinner
A alternativa C está correta.
Por meio do uso de accordion, conseguimos criar uma sequência de painéis, em que o título de cada um é
definido via tag h3 e o conteúdo em uma tag div, sendo o método aplicado a uma div que envolve todos
os pares h3/div. Por padrão, apenas um painel começa selecionado, ficando aberto (visível), enquanto os
demais apresentam apenas o título. O clique sobre o título de outro painel altera o painel selecionado,
ocultando o conteúdo dos demais.
2. CRUD utilizando o framework Thymeleaf
Framework Thymeleaf
Por meio do framework Thymeleaf, criamos templates HTML, os quais promovem uma separação natural da
camada de visualização, definindo modelos que funcionam como protótipos de design naturais. Ele trabalha com
outros formatos, além do HTML, incluindo XML, texto e Java Script, de acordo com o modo de operação
utilizado. A principal diferença ocorre ao nível das críticas quanto à formação do arquivo ou tags utilizadas.
Como podemos escolher entre diferentes dialetos, cada dialeto trará as regras de formação para o tipo de
arquivo e integração com os componentes naturais do ambiente, como os managed beans do ambiente Java
Web.
Camada model
Vamos criar um projeto do tipo Web Application, com gerenciamento pelo Maven, no NetBeans, como na imagem
a seguir. Nosso projeto terá o nome ExemploTh001, e o Group Id será com.universidade, utilizando servidor Apac
he Tomcat, com Java EE versão 7.
Criação de aplicativo Web com gerenciamento do Maven.
Agora, vamos incluir a dependência do Thymeleaf no arquivo pom.xml, com o acréscimo do fragmento
apresentado a seguir:
python
O projeto deverá apresentar problemas com a adição da dependência, com um sinal de exclamação sobre o
ícone do projeto no navegador do NetBeans. Clique com o botão direito sobre o projeto e escolha a opção "Resol
ve Project Problems".
Dica
A versão do JDK interferirá na utilização do Maven, sendo aconselhável adotar ao menos a
plataforma 1.8, que já traz o protocolo SSL atualizado.
Com o projeto configurado, vamos iniciar a codificação do aplicativo com a criação da classe Contato, no pacote
com.une sa.exemploth001.model.
org.thymeleaf
thymeleaf
3.0.15.RELEASE
java
No mesmo pacote, criaremos a classe ContatoRepositorio, simulando o acesso a uma base de dados, para que
possamos testar as interfaces gerenciadas pelo Thymeleaf.
java
As classes representam, respectivamente, uma entidade do sistema e o repositório para ela, em que o meio de
armazenamento é um HashMap estático. O repositório fornece métodos para incluir, excluir e obter a lista de
contatos, além de ser inicializada com dois contatos de exemplo.
Gerenciador para os modelos do Thymeleaf
Vamos definir um gerenciador para os modelos do Thymeleaf, com a criação da classe EngineFactory, no pacote
com.universidade.exemploth001.web.
public class Contato {
private String nome;
private String telefone;
public Contato(String nome, String telefone) {
this.nome = nome;
this.telefone = telefone;
}
public Contato() {}
public String getNome() { return nome; }
public void setNome(String nome) { this.nome = nome; }
public String getTelefone() { return telefone; }
public void setTelefone(String telefone) {
this.telefone = telefone;
}
}
public class ContatoRepositorio {
private static final HashMap contatos =
new HashMap();
static {
contatos.put("Ana",new Contato("Ana","1111-1111"));
contatos.put("Carlos", new Contato("Carlos","2222-2222"));
}
public void incluir(Contato contato){
contatos.put(contato.getNome(), contato);
}
public void excluir(String nome){
contatos.remove(nome);
}
public Collection obterTodos(){
return contatos.values();
}
}
java
A classe segue o padrão Factory, com apenas um método estático, para recebimento de um contexto Web (Servl
etContext) e retorno de um gerenciador de templates. As configurações do gerenciador indicam o uso de
sintaxe HTML, com arquivos colocados no diretório templates de WEB-INF (prefixo), utilizando extensão html (suf
ixo), além de adotar cache de uma hora.
Após a definição do objeto de configuração, o gerenciador (TemplateEngine) é instanciado e associado ao
configurador. Esse gerenciador será recebido por algum objeto capaz de tratar as requisições HTTP como um
filtro.
Comentário
Antes de chegar até o Servlet, a requisição passa pelos filtros de interceptação definidos, o que
permite automatizar ações como verificação de usuário e log de eventos.
Camada Controller
Antes de criar nosso filtro, vamos definir uma interface de comandos para nosso sistema, com o nome
IController, no pacote com.unesa.exemploth001.controller.
public class EngineFactory {
public static ITemplateEngine buildTemplateEngine(
ServletContext servletContext) {
ServletContextTemplateResolver templateResolver =
new ServletContextTemplateResolver(servletContext);
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCacheTTLMs(3600000L);
templateResolver.setCacheable(true);
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
}
java
A interface criada definirá o modelo de chamadas da camada controller, utilizado nas ações do sistema, cujas
classes ficarão no mesmo pacote de IController. Começaremos definindo a classe ContatosList:
java
Vamos acrescentar a variável listaContatos, com a listagem de contatos obtida do repositório, para que seja
utilizada no template. Em seguida, ocorre o processamento para o template "contatos", utilizando o contexto
Thymeleaf (ctx) e a stream de saída para escrita do HTML (writer).
Definiremos procedimentos parecidos para as demais ações, como ContatosIncluir.
public interface IController {
void process(final WebContext ctx,
final ITemplateEngine templateEngine,
final Writer writer);
}
// Pacotes utilizados: java.io e org.thymeleaf
public class ContatosList implements IController{
ContatoRepositorio repositorio = new ContatoRepositorio();@Override
public void process(WebContext ctx,
ITemplateEngine templateEngine, Writer writer) {
ctx.setVariable("listaContatos", repositorio.obterTodos());
templateEngine.process("contatos",ctx,writer);
}
}
java
Aqui temos a ação de inclusão de contato, em que devemos obter os parâmetros enviados pela requisição
HTTP, criação de uma entidade a partir desses dados, e uso do repositório para a inclusão. O restante do
processo é equivalente ao da ação de listagem anterior.
Nada muito diferente ocorre para a ação de exclusão, que terá o nome ContatosExcluir.
java
Assim como na inclusão, após obter o nome enviado por meio da requisição e invocar a exclusão a partir do
repositório, configuramos os dados para exibição dos contatos.
Camada View
Temos o mesmo template para as três ações definidas. Para criá-lo, vamos adicionar uma página HTML com o
nome contatos, no diretório templates, a partir de WEB-INF, de acordo com código a seguir:
public class ContatosIncluir implements IController{
ContatoRepositorio repositorio = new ContatoRepositorio();
@Override
public void process(WebContext ctx,
ITemplateEngine templateEngine, Writer writer) {
String nome = ctx.getRequest().getParameter("nome");
String telefone = ctx.getRequest().getParameter("telefone");
Contato contato = new Contato(nome,telefone);
repositorio.incluir(contato);
ctx.setVariable("listaContatos", repositorio.obterTodos());
templateEngine.process("contatos",ctx,writer);
}
}
public class ContatosExcluir implements IController{
ContatoRepositorio repositorio = new ContatoRepositorio();
@Override
public void process(WebContext ctx,
ITemplateEngine templateEngine, Writer writer) {
String nome = ctx.getRequest().getParameter("nome");
repositorio.excluir(nome);
ctx.setVariable("listaContatos", repositorio.obterTodos());
templateEngine.process("contatos",ctx,writer);
}
}
python
Temos uma página HTML comum, com um formulário contendo dois campos e uma tabela, mas devemos estar
atentos ao uso de atributos definidos no namespace do Thymeleaf, que foi configurado ao nível da raiz do
documento, com o alias th. Serão esses atributos que sofrerão modificações pelo gerenciador de templates
durante o processamento.
Começando pela tabela, temos o comando th:each, que repetirá o trecho para cada elemento de listaContatos,
Nome:
Telefone:
Nome Telefone Opcoes
Nome Telefone
Excluir
fornecida pelas ações, associando cada um à variável local contato. Em cada célula da linha, teremos o valor
preenchido, com o uso de th:text, a partir do campo específico de contato. Utilizamos ainda o campo artificial co
ntatoStat para colorização de linhas ímpares.
Comentário
Note o uso de cifrão ($) para acesso a um valor, colocado entre chaves. Também temos o uso de
arroba (@), executado localmente, o que foi utilizado na construção de um link dinâmico, com a
passagem da rota e seus parâmetros, colocados entre parênteses, definindo o endereço para
exclusão de um registro específico.
Quanto ao formulário, o único elemento relevante é o uso do atributo th:action, fornecendo a rota para inclusão,
em que o uso de arroba corrige eventuais modificações da URL.
Observamos que precisaremos do mapeamento de rotas para nossas ações, o que será feito pela classe Controll
erMappings, no pacote com.universidade.exemploth001.controller.
java
Nosso mapeamento foi definido com base em um HashMap, em que cada rota é associada a um controlador
para processamento da ação específica. A classe oferece apenas um método público, com o nome fromPath,
que recebe a rota e retorna o controlador correto.
public class ControllerMappings {
private final static HashMap controladores;
static {
controladores = new HashMap();
controladores.put("/app/contato/list", new ContatosList());
controladores.put("/app/contato/incluir", new ContatosIncluir());
controladores.put("/app/contato/excluir", new ContatosExcluir());
}
public static IController fromPath(final String path) {
return controladores.get(path);
}
}
Filtro de interceptação
Vamos definir o filtro de interceptação, com a criação da classe AppFilter, que ficará no pacote com.universidade
.exemploth001.web.
java
O mapeamento do filtro ocorre para qualquer rota iniciada com app, por meio da anotação WebFilter. Detectada
uma url com essas características, o fluxo é desviado para o método doFilter, em que é invocado o método
interno para processamento (process). O fluxo é devolvido para a cadeia de filtros quando o método retorna
false.
O processamento do filtro é iniciado com alguns testes para saber se existe uma classe de ação apropriada,
@WebFilter(urlPatterns = "/app/*")
public class AppFilter implements Filter {
private ITemplateEngine templateEngine;
@Override
public void init(final FilterConfig filterConfig)
throws ServletException {
this.templateEngine = EngineFactory.buildTemplateEngine(
filterConfig.getServletContext());
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!process((HttpServletRequest)request,
(HttpServletResponse)response)) {
chain.doFilter(request, response);
}
}
private boolean process(final HttpServletRequest request,
final HttpServletResponse response)
throws ServletException {
try {
String path = request.getServletPath();
if (!path.startsWith("/app")) { return false; }
IController controller = ControllerMappings.fromPath(path);
if (controller == null) { return false; }
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
Writer writer = response.getWriter();
WebContext context = new WebContext(request, response,
request.getServletContext());
controller.process(context, this.templateEngine, writer);
return true;
} catch (IOException e) {
throw new ServletException(e);
}
}
@Override
public void destroy() { }
}
retornando false quando um controlador não é encontrado. Caso seja encontrado, a resposta é iniciada com a
configuração do tipo de conteúdo e alguns cabeçalhos para impedir a utilização de cache no cliente.
Comentário
No passo seguinte, um objeto do tipo WebContext é instanciado, e o controlador que foi obtido
anteriormente processa a resposta, utilizando o objeto instanciado, o gerenciador de templates do
filtro e o escritor do canal de saída da resposta HTTP. Note que o gerenciador de templates é obtido
no método init do filtro, a partir de nossa classe EngineFactory.
Se o processamentonão gerar falhas, o método retorna true ao chamador, mas se uma exceção ocorre, ela é
encapsulada em um ServletException, sendo ecoada para o contexto de execução com throw. Executando o
projeto, podemos acessar o endereço http://localhost:8080/app/contato/list, obtendo a saída a seguir:
Visualização da lista de contatos no projeto ExemploTh001.
Spring Boot com Thymeleaf
Criação do projeto
Embora seja possível utilizar o NetBeans para criação de aplicativos Spring Boot, com a simples alteração do
arquivo pom.xml em um aplicativo padrão Maven algumas plataformas tornam a tarefa mais simples. Uma opção
muito interessante é o Spring Tools Suite, baseado no Eclipse, que pode ser baixado a partir do endereço https://
spring.io/tools, com a instalação fornecida no formato de um arquivo jar executável, o qual efetua uma simples
extração de arquivos.
Para criar o projeto, escolha a opção de menu File, seguida de New e Spring Starter Project, adotando o nome E
xemploTh002, além do grupo com.universidade e pacote de mesmo nome, como pode ser observado na
imagem seguinte:
Criação de projeto Spring Boot no Spring Tools Suite.
Clicando no botão Next, devemos selecionar os pacotes do Spring que serão adicionados ao arquivo de
configuração pom.xml. Utilizaremos Spring Data JPA, H2 Database, Thymeleaf e Spring Web, como observado a
seguir:
Escolha dos pacotes utilizados no projeto.
Ao clicar em Finish, teremos um projeto Spring Boot com inclusão do Thymeleaf, modelo objeto-relacional do
JPA, arquitetura MVC do Spring Web e banco de dados do tipo H2, que trabalha em memória, eliminando a
necessidade de configurações. Para testar o projeto, vamos clicar com o botão direito sobre ele, no Package
Explorer, situado na divisão esquerda do ambiente, e escolher Run As, seguido de Spring Boot App.
Poderemos observar as mensagens referentes à execução na divisão inferior, painel Console, na imagem
seguinte. Apesar de estar em execução, nosso sistema não tem rotas definidas, e a chamada ao endereço de
base (localhost:8080) retornará um erro.
Acompanhamento da execução no Console do Spring Tools Suite.
Controlador Spring Web
Vamos adicionar a classe HomeController ao pacote com.universidade, utilizando o botão direito sobre o pacote
e escolhendo New, seguido de Class.
java
Aqui definimos um controlador Spring Web, com a anotação Controller, tendo o mapeamento da raiz para o
método showHome, por meio da anotação GetMapping. No método, temos um parâmetro do tipo Model, para a
atribuição de valores que serão repassados para o template, e um retorno do tipo texto, que deverá indicar o no
me do template, sem a extensão HTML.
Configuração dos templates
No corpo do método, adicionamos o atributo data, com um objeto do tipo Date, e uma lista de valores texto no
atributo departamentos. Os valores serão enviados para o template home_tl, que deveremos criar no diretório te
mplates. Vejamos a estrutura de nosso projeto:
@Controller
public class HomeController {
@GetMapping("/")
public String showHome(Model model) {
model.addAttribute("data", new Date());
List listDepartamentos = Arrays.asList(
"Financeiro", "Comercial", "Recursos Humanos");
model.addAttribute("departamentos", listDepartamentos);
return "home_tl";
}
}
Estrutura do projeto ExemploTh002.
Para criar o arquivo, utilizamos o botão direito sobre o diretório templates, seguido de New e File, sendo
adotado o nome home_tl.html. A extensão deve ser digitada, e o conteúdo do novo arquivo é apresentado na
listagem seguinte.
python
Aqui temos a criação de uma lista HTML, em que os elementos são obtidos por meio de th:each, a partir da
coleção departamentos, fornecida pelo controlador. Cada elemento é recuperado na variável departamento,
utilizada para substituir o conteúdo valor, com o uso de th:text. Da mesma forma, recuperamos a data para
preenchimento do elemento span.
Departamentos:
valor
Página gerada em valor
•
Como os templates são estáticos, devemos parar a execução e reiniciar o servidor, o que é feito de forma rápida
pela opção relaunch, na barra de ferramentas da IDE, com um ícone mesclando os símbolos stop e run. O
resultado pode ser observado na imagem a seguir:
Visualização da página inicial com o template home_tl.html.
A sintaxe para configuração dos templates inclui muitas diretivas. Na tabela seguinte, temos os símbolos
utilizados pelo Thymeleaf, segundo a Standard Expression Syntax:
Símbolo Utilização
${...} Acessa o valor de uma variável, que pode ser local ou fornecida no Model.
*{...} Seleciona o campo de um objeto.
#{...} Recupera mensagens configuradas em um arquivo com extensão properties.
@{...} Transforma o endereço da rota em uma URL válida.
~{...} Insere o conteúdo de um fragmento HTML, viabilizando reuso no template.
Tabela: Símbolos utilizados na sintaxe do Thymeleaf.
Denis Cople.
Definição de mensagens
Para demonstrar o uso de mensagens, um recurso muito útil para a definição de mensagens padronizadas,
inclusive com suporte a idiomas, vamos criar o arquivo messages.properties no diretório resources, mesmo
diretório de application.properties, com o conteúdo seguinte:
java
main.titulo=Lista de Departamentos
Em seguida, modificamos nosso arquivo de template (home_tl.html), alterando o trecho do título, conforme
apresentado a seguir:
python
Ao executar novamente o servidor, nossa página apresentará o título definido no arquivo de propriedades, ou
seja, "Lista de Departamentos".
Aqui utilizamos o Spring Tools Suite por ser uma ferramenta mais prática para a criação dos aplicativos Spring
Boot, mas, se quiser utilizar o NetBeans, basta criar um projeto-padrão, com base no Maven, e alterar o arquivo p
om.xml, incluindo as dependências corretas, como o que é apresentado a seguir, extraído de projeto exemplo:
Departamentos
python
O exemplo demonstra a grande facilidade oferecida pelo Spring Boot, em conjunto com Thymeleaf, para a
criação de aplicativos.
Persistência e controle
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.6
com.unesa
ExemploTh002
0.0.1-SNAPSHOT
ExemploTh002
Demo project for Spring Boot
11
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
com.h2database
h2
runtime
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
Camada de persistência
Para criar nossa camada de persistência, vamos utilizar um banco de dados do tipo H2, que pode trabalhar em
memória, sem exigir configurações avançadas. Para acessar outros bancos, o arquivo application.properties
deverá ser alterado, incluindo as informações necessárias para conexão com o banco de dados e gerenciamento
de transações.
Já que estamos trabalhando no modelo MVC, nossas entidades e repositórios serão criados no pacote com.unive
rsidade.model. Começaremos definindo uma entidade com o nome Departamento:
java
Nossa classe é uma entidade JPA, o que é estabelecido pela anotação Entity, sendo incluídos os campos
idDepto, chave primária com valor gerado automaticamente, e nome. Também temos um relacionamento de um
para muitos (OneToMany) com a entidade Funcionario, expresso por um conjunto (Set) de nome funcionarios.
Precisamos, portanto, de uma segunda entidade, com o nome Funcionario:
@Entity
public class Departamento {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer idDepto;
private String nome;
@OneToMany(mappedBy="departamento")
private Set funcionarios;
public Departamento() {}
public Integer getIdDepto() { return idDepto; }
public void setIdDepto(IntegeridDepto) { this.idDepto = idDepto; }
public String getNome() { return nome; }
public void setNome(String nome) { this.nome = nome; }
}
java
Agora, temos os campos matricula, nome e dataCadastro, sendo matrícula a chave primária, além da
contraparte do relacionamento estabelecido anteriormente. Note o uso da anotação ManyToOne, especificado
como obrigatório, em um atributo de nome departamento, que é relacionado à propriedade idDepto da entidade
anterior (JoinColumn).
Cada uma das entidades precisará de um repositório JPA, que no Spring é definido por uma simples interface,
em que a comunicação com o banco é toda implementada pelo framework. Temos o conteúdo do primeiro
repositório apresentado a seguir:
java
Definimos uma herança para a interface JpaRepository, com a especificação do tipo da entidade e de sua chave
primária. Ocorrerá o mesmo para o segundo repositório:
@Entity
public class Funcionario {
@Id
private String matricula;
private String nome;
private Date dataCadastro;
@ManyToOne
@JoinColumn(name="idDepto", nullable=false)
private Departamento departamento;
public Funcionario() {}
public String getMatricula() { return matricula; }
public void setMatricula(String matricula) {
this.matricula = matricula;
}
public String getNome() { return nome; }
public void setNome(String nome) { this.nome = nome; }
public Date getDataCadastro() { return dataCadastro; }
public void setDataCadastro(Date dataCadastro) {
this.dataCadastro = dataCadastro;
}
public Departamento getDepartamento() { return departamento; }
public void setDepartamento(Departamento departamento) {
this.departamento = departamento;
}
}
public interface DepartamentoRepository extends
JpaRepository{
}
java
Camada de controle
Nossa camada model está completa e já podemos criar a classe DepartamentoController, no pacote com.universi
dade.controller, representando nosso primeiro controlador.
java
Temos um controlador capaz de interceptar as rotas HTTP, por meio do mapeamento dos métodos pelas
anotações GetMapping e PostMapping. Temos ainda o relacionamento com um repositório, instanciado no
contexto do Spring por meio da anotação Autowired.
public interface FuncionarioRepository extends
JpaRepository{
}
@Controller
public class DepartamentoController {
@Autowired
DepartamentoRepository repositorio;
@GetMapping("/departamentos")
public String obterTodos(Model model) {
model.addAttribute("departamentos", repositorio.findAll());
return "deptolist";
}
@GetMapping("/departamentos/excluir")
public String excluir(Model model, Integer idDepto) {
repositorio.deleteById(idDepto);
model.addAttribute("departamentos", repositorio.findAll());
return "deptolist";
}
@GetMapping("/departamentos/cadastro")
public String cadastrar(Model model) {
model.addAttribute("depto", new Departamento());
return "deptodata";
}
@PostMapping("/departamentos/cadastro")
public String cadastrar(Model model,
@ModelAttribute("depto") Departamento depto) {
repositorio.save(depto);
model.addAttribute("departamentos", repositorio.findAll());
return "deptolist";
}
}
Comentário
O primeiro mapeamento ocorre para a rota /departamentos, em que o método de reposta adicionará
uma lista de departamentos aos dados da página, obtida pelo repositório por meio de findAll,
retornando em seguida o nome do template. Temos na rota /departamentos/excluir um
comportamento muito similar, mas ocorrendo a exclusão do departamento selecionado a partir de
idDepto, com base no método deleteById do repositório.
A inclusão utiliza a rota /departamentos/cadastro nos modos GET e POST, em que o modo GET associará um
atributo do tipo Departamento aos dados da página para cadastro, cujo nome é retornado ao final, enquanto no
modo POST recebemos os dados do formulário já preenchido em um parâmetro depto, do tipo Departamento,
anotado como ModelAttribute, procedendo com a persistência dos dados por meio do método save do
repositório, obtenção do conjunto de entidades atualizado via findAll, e retorno do template para listagem.
Ainda não criamos nossos templates, mas a execução do aplicativo irá gerar as tabelas do banco H2, assim
como faria em qualquer outro banco, com a estrutura a seguir:
Estrutura do banco gerado pelo sistema.
Como o banco é recriado a cada execução, por estar trabalhando em memória, precisamos de alguns dados de
teste, o que será obtido com a execução de alguns comandos SQL. Teremos de adicionar um arquivo com o
nome import.sql, na raiz de resources, contendo a listagem a seguir, sem quebras de linha nos comandos:
sql
Algo interessante em nossa camada de controle é o fato de que o mesmo método utilizado para incluir um
registro permite também alterá-lo. Com base nessa característica, para adicionar a possibilidade de edição no
sistema, basta inserir um segundo mapeamento para a rota que inicia o template para preenchimento de dados,
mas com o acréscimo de um segmento com o identificador, permitindo fornecer os dados atuais da entidade.
O método adicionado pode ser observado na listagem seguinte, para a classe DepartamentoController:
java
O mesmo tipo de modificação pode ser efetuado em FuncionarioController, adicionando o método apresentado
a seguir. Note que, em ambos os casos, temos a definição da variável que receberá o segmento com a anotação
PathVariable:
java
Com as camadas de persistência e controle prontas, podemos iniciar a construção da camada de visualização,
ou seja, dos templates. Vejamos a estrutura de projeto até o momento:
INSERT INTO DEPARTAMENTO VALUES (-1,'Financeiro');
INSERT INTO DEPARTAMENTO VALUES (-2,'Comercial');
INSERT INTO FUNCIONARIO (MATRICULA,NOME,DATA_CADASTRO,ID_DEPTO) VALUES
('ALFA001','Ana',null,-1);
INSERT INTO FUNCIONARIO (MATRICULA,NOME,DATA_CADASTRO,ID_DEPTO) VALUES
('ALFA002','Carlos',null,-1);
INSERT INTO FUNCIONARIO (MATRICULA,NOME,DATA_CADASTRO,ID_DEPTO) VALUES
('ALFA003','Maria',null,-2);
INSERT INTO FUNCIONARIO (MATRICULA,NOME,DATA_CADASTRO,ID_DEPTO) VALUES
('BETA001','Paulo',null,-2);
INSERT INTO FUNCIONARIO (MATRICULA,NOME,DATA_CADASTRO,ID_DEPTO) VALUES
('BETA002','Beatriz',null,-1);
@GetMapping("/departamentos/cadastro/{idDepto}")
public String cadastrar(Model model,
@PathVariable Integer idDepto) {
Departamento depto = repositorio.findById(idDepto).get();
model.addAttribute("depto", depto);
return "deptodata";
}
@GetMapping("/funcionarios/cadastro/{matricula}")
public String cadastrar(Model model,
@PathVariable String matricula) {
Funcionario funcionario = repositorio.findById(matricula).get();
model.addAttribute("deptos",repositorioDeptos.findAll());
model.addAttribute("funcionario", funcionario);
return "funcdata";
}
Projeto com as camadas de controle e persistência.
Interface do CRUD
Templates para departamentos
Agora, vamos criar nossa camada de visualização, que será constituída pelos templates requeridos na camada
de controle, com o nome definido a partir do retorno de cada método. Todos os arquivos serão criados no
diretório templates, a partir de resources, com o clique do botão direito sobre o diretório e escolha da opção
New, seguida de File.
Nosso primeiro template será definido no arquivo deptolist.hmtl.
python
Temos a definição de um link para inclusão de departamentos, com uso de arroba e indicação da rota /
departamento/cadastro no atributo th:href, seguido da definição de uma tabela para exibição dos dados. Na
tabela, teremos outros links para exclusão e alteração de um departamento. A exclusão utiliza a sintaxe para uso
de parâmetros na requisição, enquanto a alteração trabalha com fornecimento da chave em um segmento da
rota.
Por meio de th:each, recuperamos os departamentos que foram enviados pelo