O JSF (JavaServer Faces) é o framework MCV para Java mais utilizado atualmente nas médias e grandes corporações para construção de interfaces com usuários baseando-se em componentes para aplicações web. O JSF faz parte da Java Platform, Enterprise Edition. Entre outras coisas o JSF inclui suporte a internacionalização e acessibilidade, um conjunto padrão de componentes de interface de usuário que possibilitam validação padronizada, um modelo de eventos no lado servidor ("server-side event model"), gerência de estados, Managed Beans e muito mais.
Server e Client Extension Points
Os Converters, Validators, e Listeners são objetos que estão no lado servidor e que adicionam funcionalidades aos componentes nas páginas.
Os comportamentos são pontos de extensão do lado cliente que podem melhorar o conteúdo processado de um componente através de scripts definidos pelo comportamento.
Um Converter converte os dados inseridos em um componente de um formato para outro. Por exemplo String para Number. JSF provê diversos Converters tais como f:convertNumber e f:convertDateTime. Eles podem ser aplicados a qualquer componente editável. Segue na Listagem 1 um exemplo.
Listagem 1. Exemplo de página utilizando um converter.
<h:form>
Idade:
<h:inputText value="#{usuario.idade}" id="idade">
<f:convertNumber integerOnly="true"/>
</h:inputText>
<h:commandButton value="Enviar"/>
</h:form>
No código acima verificamos que o texto inserido na caixa de texto será convertido para um Integer se for possível.
Uma mensagem de erro será lançada se o texto não puder ser convertido.
Um Converter customizado pode ser facilmente criado, conforme é exemplificado na Listagem 2.
Listagem 2. Exemplo de como podemos criar um converter
@FacesConverter("meuConverterExemplo")
public class MeuConverterExemplo implements Converter {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
//. . .
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
//. . .
}
}
No código acima os métodos getAsObject e getAsString executam as conversões Object-para-String e String-para-Object respectivamente.
A classe POJO implementa uma interface Converter e também é marcada com a anotação @FacesConverter. Agora este converter já poderia ser utilizado numa página JSF como mostra o código da Listagem 3.
Listagem 3. Exemplo de página utilizando um converter criado.
<h:inputText value="#{usuario.idade}" id="idade">
<f:converter converterId=" meuConverterExemplo "/>
</h:inputText>
O valor de @FacesConverter deve ser igual ao valor do atributo "converterId" na página JSF acima.
Já um validador é usado para validar informações que são recebidas por componentes de entrada (inputText). O JSF fornece vários validadores pré-definidos tais como “f:validateLength” e “f:validateDoubleRange”.
Esses validadores podem ser aplicados em qualquer componente editável. Na Listagem 4 temos um exemplo.
Listagem 4. Exemplo de um validador sendo usado numa página.
<h:inputText value="#{usuario.nome}" id="nome">
<f:validateLength min="3" maximum="15"/>
</h:inputText>
Neste código o tamanho do texto inserido será validado se ele está entre 3 e 15 caracteres.
Uma mensagem de erro é lançada se o tamanho do texto estiver fora do tamanho especificado.
Um validador customizado também pode ser criado. O código da Listagem 5 demonstra como poderíamos fazer um validador.
Listagem 5. Criando um validador.
@FacesValidator("meuValidadorExemplo")
public class MeuValidadorExemplo implements Validator {
@Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
//. . .
}
}
Neste código o método “validate” retorna se o valor for validado com sucesso. Caso contrário, uma exceção “ValidatorException” será lançada.
Este validador pode ser aplicado a qualquer componente editável, conforme exemplificado na Listagem 6.
Listagem 6. Utilizando um validador customizado.
<h:inputText value="#{usuario.nome}" id="nome">
<f:validator id="meuValidadorExemplo"/>
</h:inputText>
O valor de @FacesValidator deve ser igual ao valor do atributo id de “f:validator”.
JSF também fornece integração com restrições pré-definidas usando Bean Validation.
Além de colocar anotações de restrições no bean, nenhum outro trabalho adicional é necessário pelo desenvolvedor. Qualquer mensagem de erro por causa da violação de restrição é automaticamente convertido para um FacesMessage e exibido para o usuário final.
A tag “f:validateBean” pode ser utilizada para especificar “validationGroups” para indicar quais grupos de validação deveriam ser levados em consideração quando validamos um componentes em particular.
Outro objeto bastante utilizado são os listeners. Um listener monitora eventos em um componente. O evento pode ser um valor alterado, um clique de um botão, um clique num link, entre outras coisas. Um listener pode ter um método em um Managed Bean ou uma Classe.
Um ValueChangeListener pode ser registrado em um componente editável. O código da Listagem 7 mostra um exemplo.
Listagem 7. Utilizando valueChangeListener em um componente editável.
<h:inputText value="#{ usuario.idade}" id="idade" valueChangeListener="#{usuario.nomeAtualizado}">
Neste código o método “nomeAtualizado” no bean “Usuario” é chamado quando o formulário associado é enviado.
Podemos criar um listener em nível de classe implementando a interface “ValueChangeListener” e especificá-lo na página usando a tag “f:valueChangeListener”.
Por outro lado os converters, validadores e listeners, melhoram a funcionalidade no lado do cliente de um componente anexando scripts declarativamente. Por exemplo, a tag “f:ajax” é definida como um comportamento do lado cliente.
Comportamento do lado cliente também permite executar validações no lado cliente, além de logging, exibir dicas de ferramentas entre coisas. Podemos definir um comportamento customizado estendendo “ClientBehaviorBase” e anotando com @FacesBehavior.
Resource Library Contracts
JSF 2, por padrão, introduziu o Facelets como a Linguagem de Declaração da View (View Declaration Language). Com Facelets podemos criar templates usando XHTML e CSS que podem ser usados para fornecer uma aparência consistente em diferentes páginas de uma aplicação.
O JSF 2.2 define o Resource Library Contracts, uma biblioteca de templates e recursos associados que podem aplicados na aplicação inteira de uma maneira reutilizável e intercambiável.
Um conjunto configurável de Views na aplicação serão declarados como template-clientes de qualquer template no Resource Library Contract.
Um conjunto configurável de Views na aplicação será capaz de declarar-se como modelo-clientes de qualquer modelo no contrato da biblioteca de recursos (Resource Library Contracts).
O Resource Library Contracts reside nos diretórios "contracts" da raiz da aplicação web.
Segue um exemplo abaixo de uma estrutura de exemplo de uma aplicação:
index.xhtml
novo/index.xhtml
contracts/modeloazul/layout.css
contracts/modeloazul/template.xhtml
contracts/modeloazul/footer.png
contracts/modelovermelho
contracts/modelovermelho/layout.css
contracts/modelovermelho/template.xhtml
contracts/modelovermelho/logo.png
WEB-INF/faces-config.xml
Essa hierarquia mostra que primeiramente a aplicação tem duas páginas: index.xhtml e novo/index.xhtml. Essas são páginas de modelo do cliente.
Também podemos observar que todos os contratos residem no diretório "contracts" do WAR e todos os templates e recursos para um contrato estão em seus próprios diretórios. Por exemplo, no exemplo anterior temos dois contratos definidos, modeloazul e modelovermelho. Além disso, cada contrato tem um arquivo template.xhtml, um CSS, e uma imagem. Cada template é chamado como um “template declarado”. No template, é recomendado que se use h:outputStylesheet quando referenciarmos um CSS para evitarmos alguns erros desnecessários quando não utilizamos essa tag. O arquivo template.xhtml tem tags chamadas como pontos de inserção declarados. Os arquivos CSS, imagens e outros recursos agrupados no diretório são recursos declarados.
Dessa forma, o template declarado, pontos de inserção declarados e os recursos declarados, juntos definem o Resource Library Contract. Um template cliente precisa saber o valor de todos os três a fim de utilizar o contrato. As páginas clientes usarão o contrato referenciando o template. Segue o exemplo abaixo de como podemos fazer essa referência:
<ui:composition template="/template.xhtml">
<ui:define name="content">
. . .
</ui:define>
</ui:composition>
Na hierarquia anterior também podemos notar que existe o WEB-INF/faces-config.xml. O arquivo faces-config define o uso do contrato. Segue na Listagem 8um exemplo do faces-config.
Listagem 8. Exemplo do arquivo faces-config com as configurações do contrato.
<application>
<resource-library-contracts>
<contract-mapping>
<url-pattern>/novo/*</url-pattern>
<contracts>modeloazul</contracts>
</contract-mapping>
<contract-mapping>
<url-pattern>*</url-pattern>
<contracts>modelovermelho</contracts>
</contract-mapping>
</resource-library-contracts>
</application>
Um contrato é aplicado baseando-se num padrão de URL que é invocado na aplicação. Com base nesta configuração, tem-se que o contrato modelovermelho deverá ser aplicado para faces/index.xhtml e o contrato modeloazul será aplicado para faces/novo/index.xhtml.
Os contratos podem ser empacotados em META-INF/contracts de um arquivo JAR. Cada contrato no JAR deve ter um arquivo marcador.
O nome do arquivo é dado pelo valor da constante “javax.faces.application.ResourceHandler.RESOURCE_CONTRACT_XML”. Segue um exemplo abaixo de como os contratos estão empacotados no JAR:
META-INF/contracts/modeloazul/javax.faces.contract.xml
META-INF/contracts/modeloazul/layout.css
META-INF/contracts/modeloazul/template.xhtml
META-INF/contracts/modeloazul/footer.png
META-INF/contracts/modelovermelho/javax.faces.contract.xml
META-INF/contracts/modelovermelho/layout.css
META-INF/contracts/modelovermelho/template.xhtml
META-INF/contracts/modelovermelho/logo.png
O conteúdo dos diretórios de contratos das aplicações pode ser empacotado em META-INF/contracts do JAR. Este JAR ainda pode ser empacotado em WEB-INF/lib e os templates declarados podem ser usados na aplicação. Segue abaixo um exemplo de uma aplicação que referencia o JAR anteriormente criado:
index.xhtml
novo/index.xhtml
WEB-INF/lib/layout.jar
Podemos usar um novo arquivo layout.jar, fornecendo um conjunto similar de pontos de inserção declarados e recursos (provavelmente com um CSS diferente), para alterar a aparência da aplicação.
Também podemos alterar o template da página dinamicamente colocando a página template do cliente em um f:view. Segue na Listagem 9 um exemplo.
Listagem 9. Exemplo utilizando a tag f:view.
<f:view contracts="#{contractsBean.contract}">
<ui:composition template="/template.xhtml">
<ui:define name="content">
. . .
<h:form>
<h:selectOneRadio value="#{contractsBean.contract}">
<f:selectItem itemValue="modelovermelho" itemLabel="red"/>
<f:selectItem itemValue="modeloazul" itemLabel="blue"/>
</h:selectOneRadio>
<h:commandButton value="Aplicar Modelo" action="index" />
</h:form>
</ui:define>
</ui:composition>
</f:view>
Neste código o f:view tem um atributo conatracts que é ligado em uma EL.
O valor desta EL é populado por um radio button no formulário dentro de ui:define. O valor do radio button é igual ao nome do contrato. Se clicarmos no “command button” será aplicado o template escolhido para esta página.
Na Listagem 10 temos a definição do bean ContractsBean referenciado acima.
Listagem 10. Exemplo do bean ContractsBean.
@Named
@SessionScoped
public class ContractsBean implements Serializable {
String contract = "modelovermelho";
// getter & setter
}
Faces Flow
O Faces Flow foi introduzido no JSF 2.2. Esta funcionalidade tem conceitos chaves de outras tecnologias tais como ADF Task Flows, Spring Web Flow, e Apache MyFaces CODI. O objetivo do Faces Flow é fornecer uma abordagem modular para a definição de fluxo de controle em um aplicativo. Outro objetivo do Faces Flow é de padronizar essa abordagem como parte integrante do JSF 2.2.
O Faces Flow fornece um encapsulamento de páginas relacionadas e backing beans correspondentes como um módulo. Este módulo tem pontos de entrada e saída bem definidos atribuídos pelo desenvolvedor da aplicação.
Frequentemente os objetos em um Faces Flow são projetados para permitir que o usuário realize uma tarefa que requer entrada sobre um número de diferentes Views. Uma aplicação torna-se assim uma coleção de fluxos ao invés de apenas Views.
Podemos imaginar um carrinho de compras com várias páginas. Uma página será para selecionar os itens que desejamos comparar, uma segunda página para a escolha de opções de envio, uma terceira página para inserir os detalhes do cartão de crédito, e uma quarta página de confirmação da encomenda.
Podemos usar Managed Beans para capturar dados, variáveis com escopo de sessão para passar informações entre as páginas, cliques em botões para invocar lógica de negócio nos EJBs, e regras de navegação para ir de uma página para outra.
Existem alguns problemas com esta abordagem onde todo este fluxo de sequencia tipicamente será parte de uma grande aplicação, esta aplicação tipicamente com várias páginas, é um grande fluxo e tudo tem uma visibilidade global sem particionamento lógico. Além disso, o fluxo de páginas ou Views não pode ser encapsulado como uma unidade lógica e dessa forma não pode ser reutilizado, incorporado dentro de outra aplicação maior ou “fluir” facilmente. O mesmo fluxo não pode ser aberto em múltiplas janelas porque as variáveis com escopo de sessão são usadas para passar informações entre as páginas. O CDI define o @ConversationScoped, mas isso é apenas parte da solução que veremos posteriormente. Outro problema dessa abordagem é que um escopo de requisição é para uma requisição particular e um escopo baseado em sessão é mais do que um escopo de requisição, mas torna-se inválido depois que um browser é fechado. Assim sendo, precisaríamos de um escopo entre os dois, que pode se estender por várias páginas. Por fim, a única forma de invocar a lógica da aplicação é através de um componente de interface com usuário ativada pelo próprio usuário em uma página, portanto, a lógica de negócio não pode ser invocada sem qualquer ação iniciada pelo usuário.
O Faces Flow oferece algumas soluções para essas questões, são elas:
- A aplicação é dividida em uma série de fluxos modulares que podem chamar um ao outro.
- O fluxo de páginas pode ser empacotado como um módulo que pode ser reutilizado dentro da mesma aplicação ou em uma aplicação totalmente diferente.
- O escopo de memória compartilhada permite que as informações sejam passadas entre Views dentro de um fluxo de tarefa.
- Um novo escopo CDI, @FlowScoped, pode ser especificado no bean. Este escopo permite ativação/passivação automática do bean como o escopo é inserido/encerrado.
- A lógica de negócio pode ser invocada de qualquer lugar na página com base na definição do fluxo.
O fluxo da aplicação não é restrito apenas ao fluxo entre páginas, ao invés disso é definida como fluxo entre "nós".
Existem cinco tipos diferentes de nós, são eles:
- View: Qualquer página JSF na aplicação.
- Method call: Invoca a lógica da aplicação do gráfico de fluxo (flow graph) através de uma EL.
- Switch: Decisões de navegação no gráfico de fluxo baseado em booleano da EL.
- Flow call: Chama outro fluxo com os parâmetros e recebe valores de retorno.
- Flow return: Retorna para o fluxo de chamadas.
Esses nós definem pontos de entrada e saída de um fluxo.
O mais novo escopo introduzido no CDI é o @FlowScoped que define o escopo de um bean no fluxo especificado. Isto habilita a ativação/passivação automática do bean quando o escopo é inserido/encerrado, conforme a Listagem 11.
Listagem 11. Exemplo utilizando a anotação FlowScoped.
@FlowScoped("fluxo1")
public class MeuFluxo1Bean {
String endereco;
String cartaoCredito;
//. . .
}
Neste código, o bean tem duas variáveis com escopo “flow”: endereco e cartaoCredito. O bean é definido para o fluxo “fluxo1”.
Um novo objeto EL para armazenamento de fluxo #{flowScope} também foi introduzido no JSF 2.2. Este objeto mapeia para facesContext.getApplication().getFlowHandler().getCurrentFlowScope().
Na Listagem 12 temos um exemplo.
Listagem 12. Exemplo utilizando flowScope.
Valor: <h:inputText id="input" value="#{flowScope.valor}" />
Neste código, o valor inserido na caixa de texto é ligado ao #{flowScope.valor}. Esta expressão EL pode ser utilizada em outras páginas do fluxo para acessar o valor.
Podemos definir fluxos declarativamente usando , ou programaticamente usando a API FlowBuilder. Os dois mecanismos são mutuamente exclusivos.
Fluxos podem ser empacotados em arquivos JAR ou em diretórios. Os pacotes JAR requerem que os fluxos sejam explicitamente declarados no META-INF/faces-config.xml dentro do arquivo JAR. Os Nós de fluxo são empacotados em META-INF/flows/ onde é uma entrada do diretório do JAR cujo nome é idêntico ao de um ID de fluxo nas classes “FlowDefinition” correspondentes.
Se o bean @FlowScoped e um fluxo definido via FlowBuilder estão empacotados num arquivo JAR, eles devem ser acompanhados por META-INF/beans.xml. Segue abaixo um exemplo de um JAR com seus respectivos diretórios e arquivos:
META-INF/faces-config.xml
META-INF/flows/fluxo1/entrada.xhtml
META-INF/flows/fluxo1/proximo.xhtml
META-INF/flows/fluxo1/fim.xhtml
META-INF/flows/fluxo2/inicio.xhtml
META-INF/flows/fluxo2/proximo.xhtml
META-INF/flows/fluxo2/fim.xhtml
META-INF/beans.xml
org/glassfish/exemplo/MeuFluxo1Bean.class
org/glassfish/exemplo/MeuFluxo2Bean.class
org/glassfish/exemplo/MeuFluxo1Definicao.class
org/glassfish/exemplo/MeuFluxo2Definicao.class
Neste JAR podemos notar os seguintes destaques abaixo:
- Existem dois fluxos, fluxo1 e fluxo2.
- O arquivo META-INF/beans.xml é obrigatório para habilitar o CDI. Isto pode ser habilitado de forma implícita se as anotações do bean são usadas no arquivo.
- MeuFluxo1Bean e MeuFluxo2Bean são beans com escopo de fluxo. Esses beans são usados para armazenar qualquer informação de fluxo local.
- MeuFluxo1Definicao define pontos de entrada e saída de um fluxo, nome do parâmetro de entrada e valor proveniente de um outro fluxo, nome do parâmetro de saída e o valor para um outro fluxo e de navegação para outros nós.
Na Listagem 13 temos um código de exemplo das definições de um fluxo.
Listagem 13. Exemplo de implementação do MeuFluxo1Definicao.
public class MeuFluxo1Definicao {
@Produces @FlowDefinition
public Flow defineFlow(@FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "fluxo1";
flowBuilder.id("unique", flowId);
flowBuilder.viewNode(flowId, "/" + flowId + "/" + flowId + ".xhtml").markAsStartNode();
flowBuilder.returnNode("goHome").
fromOutcome("#{fluxo1Bean.homeValue}");
flowBuilder.inboundParameter("param1DoFluxo2", "#{flowScope.param1Value}");
flowBuilder.flowCallNode("call2").flowReference("", "fluxo2").
outboundParameter("param1DoFluxo1", "param1 fluxo1 valor");
return flowBuilder.getFlow();
}
}
Neste código temos um fluxo definido programaticamente via CDI Producer. @FlowDefinition é uma anotação a nível de classe que permite que o fluxo seja definido através da API Flow Builder. No código acima também temos uma instância de FlowBuilder sendo injetada como um parâmetro via @FlowBuilderParameter e sendo usado para definir o fluxo.
O “fluxo1” é definido como o identificador do fluxo e fluxo1.xhtml é marcado como o nó de inicio. O método returnNode é usado para definir um ponto de saída do fluxo. Neste caso, o fluxo é direcionado para /index para a ação goHome. O valor do nó pode ser especificado como uma expressão EL. Os parâmetros de entrada nomeados são chamados “parâmetros de outro fluxo” e são definidos através do método “inboundParameter”. O valor do método é populado em outro lugar com um elemento “outboundParameter” correspondente. O valor é armazenado no armazenamento de fluxo local via #{flowScope}.
Por fim, o método flowCallNode é usado para definir um ponto de saída de um fluxo. Neste caso, o fluxo “fluxo2” é chamado. Um parâmetro de saída nomeado e seu valor são definidos através do método “outboundParameter”.
Da mesma forma a classe MeuFluxo2Definicao define fluxo2.
Os fluxos empacotados em diretórios usam uma convenção para as configurações. As convenções são:
- Todo arquivo "View Declaration Language", definido por uma página “.xhtml”, nesse diretório é um nó View desse fluxo.
- O nó de início do fluxo é a View cujo nome é o mesmo que o nome do fluxo.
- A navegação entre qualquer uma das Views no diretório é considerado dentro do fluxo.
- Navegação para uma View de fora desse diretório é considerado uma saída do fluxo.
- Um arquivo opcional -fluxo.xml representa a definição do fluxo.
As regras neste arquivo sobrescrevem as convenções descritas:
fluxo1/fluxo1.xhtml
fluxo1/fluxo1a.xhtml
fluxo1/fluxo1b.xhtml
fluxo2/fluxo2-fluxo.xml
fluxo2/fluxo2.xhtml
fluxo2/fluxo2a.xhtml
fluxo2/fluxo2b.xhtml
WEB-INF/...
Seguindo as convenções definidas nestes diretórios tem-se que:
- fluxo1.xhtml é o nó inicial do fluxo “fluxo1”, e “fluxo1a” e “fluxo1b” são dois outros nós no mesmo fluxo.
- “fluxo2” é o nó inicial do fluxo “fluxo2”, e “fluxo2a” e “fluxo2b” são outros dois nós no mesmo fluxo. “fluxo2/fluxo2-fluxo.xml” define a navegação declarativa do fluxo “fluxo2”. Isto define os pontos de entrada e saída de um fluxo, nome do parâmetro de entrada e valor proveniente de outro fluxo, nome do parâmetro de saída e o valor para outro fluxo e a navegação para outros nós.
Segue na Listagem 14 um exemplo do arquivo faces-config que define o fluxo de forma declarativa.
Listagem 14. Exemplo do arquivo faces-config.
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<flow-definition id="fluxo1">
<flow-return id="goHome">
<from-outcome>/index</from-outcome>
</flow-return>
<inbound-parameter>
<name>param1DoFluxo1</name>
<value>#{flowScope.param1Value}</value>
</inbound-parameter>
<flow-call id="callFlow2">
<flow-reference>
<flow-id>fluxo2</flow-id>
</flow-reference>
<outbound-parameter>
<name>param1DoFluxo1</name>
<value>param1 fluxo1 valor</value>
</outbound-parameter>
</flow-call>
</flow-definition>
</faces-config>
Neste código temos:
- A tag que define o fluxo para “fluxo1”, identificado pelo atributo id.
- A tag que define um ponto de saída do fluxo. Neste caso, o fluxo é direcionado para /index para a ação goHome. O valor do nodo pode ser especificado como uma expressão EL.
- Um parâmetro de entrada nomeado que é definido via . Este valor é populado em outro lugar com um elemento correspondente. O valor é armazenado no armazenamento local do fluxo via #{flowScope}.
- A tag que define um ponto de saída do fluxo. Neste caso, o fluxo “fluxo2” é chamado. Um parâmetro de saída nomeado e o seu valor são configurados via .
- O diretório WEB-INF que conterá outros recursos requeridos pelas páginas, tais como os beans.
Atributos e Marcações HTML 5
O HTML 5 adiciona uma série de novos atributos para elementos existentes. Esses atributos incluem o tipo do atributo para elementos de entrada (input), além do suporte a valores tais como email, url, telefone, número, range, e data.
Veja um exemplo na Listagem 15.
Listagem 15. Exemplo utilizando o type email.
<input type="email" name="meuEmail"/>
O código acima permite que o browser verifique se o texto inserido está no formato de e-mail.
Além disso, atributos de dados customizados, podem ser definidos para armazenar informações privadas customizadas para a página ou para a aplicação.
Todo elemento HTML pode ter qualquer número de atributos de dados customizados especificados, com qualquer valor, por exemplo:
<input id="minhaData" type="text" data-length="3"/>
Este fragmento de código introduz o tamanho da informação como um atributo de dados customizado.
Esses atributos não são processados, mas podem ser lidos via JavaScript. Este atributo pode ser acessado em JavaScript como:
document.getElementById("minhaData").dataset.length;
Anteriormente ao JSF 2.2, por default, estes tipos recentemente introduzidos e os atributos de dados não eram suportados pelos componentes. O conjunto de atributos disponíveis suportados por um componente JSF é determinado por uma combinação do UIComponent e Renderer para aquela tag. Em alguns casos, o valor do atributo é interpretado pelo UIComponent ou Renderer (por exemplo, atributo columns do h:panelGrid), e em outros, o valor é passado diretamente para o agente de usuário (por exemplo, o atributo lang de h:inputText). Em ambos os casos, o UIComponent e o Renderer tem um conhecimento anterior do conjunto de atributos permitidos.
O JSF 2.2 introduz os “atributos de passagem”, na qual nos permite listar arbitrariamente pares nome/valor num componente que são passados diretamente para o agente de usuário sem interpretação pelo UIComponent ou Renderer. Os atributos de passagem podem ser especificados em três formas diferentes:
- Prefixando o atributo com o nome abreviado atribuído para o
namespace XML http://xmlns.jcp.org/jsf/passThrough.
Segue um exemplo na Listagem 16.
Listagem 16. Exemplo prefixando o atributo com o nome abreviado.Neste código, p é uma abreviação para o namespace.<h:inputText p:type="email" value="#{usuario.email}"/>
- Aninhando a tag dentro de um
componente, conforme a Listagem 17.]
Listagem 17. Exemplo aninhando a tag dentro de um componente.Neste código, um atributo de tipo de valor de e-mail é marcado como um atributo de passagem.<h:inputText value="#{usuario.email}"> <f:passThroughAttribute name="type" value="email"/> </h:inputText>
- Aninhando a tag dentro de um
componente com o valor definido para um Map, conforme a Listagem 18.
Listagem 18. Exemplo aninhando a tag dentro de um componente com valor definido para um Map.#{usuario.meusAtributos} deve apontar para um Map onde os valores podem ser ou literais ou expressões.<h:inputText value="#{usuario.data}"> <f:passThroughAttributes value="#{usuario.meusAtributos}"/> </h:inputText>
Este mecanismo pode ser aplicado para qualquer componente JSF e não é restrito apenas para elementos HTML 5.
Component Tags do JSF 2
JSF 2 possui uma variedade de componentes. Cada componente possui por trás uma classe UIComponent. Esses componentes são exibidos como elementos HTML.
Veremos neste artigo os componentes mais utilizados, os novos componentes adicionados e como eles são exibidos em HTML.
1. Componente h:commandButton
Exemplo de Uso:
<h:commandButton value="Próximo" action="proximo"/>
Saída HTML:
<input type="submit" name="j_idt8" value="Próximo" />
2. Componente h:commandLink
Exemplo de Uso:
<h:commandLink value="meuLink" action="proximo"/>
Saída HTML:
<script type="text/javascript" src="/components/faces/javax.faces.resource/jsf.js?ln=javax.faces&stage=Development">
</script>
<a href="#" onclick="mojarra.jsfcljs(document.getElementById('j_idt6'), {'j_idt10':'j_idt10'},'');return false">
meuLink
</a>
3. Componente h:dataTable
Exemplo de Uso:
<h:dataTable value="#{meuBean.lista}" var="p">
<h:column>
<f:facet name="header">Id</f:facet>
#{p.id}
</h:column>
<h:column>
<f:facet name="header">Nome</f:facet>
#{p.nome}
</h:column>
</h:dataTable>
Saída HTML:
<table>
<thead>
<tr>
<th scope="col">Id</th>
<th scope="col">Nome</th>
</tr>
</thead>
<tbody>
<tr>
<td> 1 </td>
<td> João </td>
</tr>
<tr>
<td> 2 </td>
<td> Carlos </td>
</tr>
<tr>
<td> 3 </td>
<td> Francisco </td>
</tr>
</tbody>
</table>
4. Componente h:form
Exemplo de Uso:
<h:form> ... </h:form>
Saída HTML:
<form id="j_idt6" name="j_idt6" method="post"
action="/components/faces/index.xhtml"
enctype="application/x-www-form-urlencoded">
...
</form>
5. Componente h:graphicImage
Exemplo de Uso:
<h:graphicImage value="/images/teste.png"/>
Saída HTML:
<img src="/components/images/teste.png" />
6. Componente h:inputHidden
Exemplo de Uso:
<h:inputHidden value="valorEscondido"/>
Saída HTML:
<input type="hidden" name="j_idt22" value="valorEscondido" />
7. Componente h:inputSecret
Exemplo de Uso:
<h:inputSecret value="#{meuBean.senha}"/>
Saída HTML:
<input type="senha" name="j_idt24" value="" />
8. Componente h:inputText
Exemplo de Uso:
<h:inputText id="meuInputTextExemplo" value="#{meuBean.inputText}"/>
Saída HTML:
<input id="meuInputTextExemplo" type="text" name="meuInputTextExemplo" />
9. Componente h:inputTextArea
Exemplo de Uso:
<h:inputTextarea value="#{meuBean.inputTextarea}"/>
Saída HTML:
<textarea name="j_idt27">
</textarea>
10. Componente h:button
Exemplo de Uso:
<h:button value="meuButton" outcome="next"/>
Saída HTML:
<input type="button" onclick="window.location.href='/components/faces/next.xhtml'; return false;" value="meuButton" />
11. Componente h:link
Exemplo de Uso:
<h:link value="Próximo" outcome="proximo">
<f:param name="teste" value="testeValor"/>
</h:link>
Saída HTML:
<a href="/components/faces/next.xhtml?teste=testeValor">
Próximo
</a>
12. Componente h:body
Exemplo de Uso:
<h:body>
Teste do Corpo
</h:body>
Saída HTML:
<body>
Teste do Corpo
</body>
13. Componente h:outputFormat
Exemplo de Uso:
<h:outputFormat value="Olá {0}">
<f:param value="João"/>
</h:outputFormat>
Saída HTML:
Olá João
14. Componente h:outputLabel
Exemplo de Uso:
<h:outputLabel for="inputTextId" value="meuLabelExemplo"/>
<h:inputText id="inputTextId" value="meuInputTextExemplo"/>
Saída HTML:
<label for="inputTextId">
meuLabelExemplo
</label>
<input id="inputTextId" type="text" name="inputTextId" value="meuInputTextExemplo" />
15. Componente h:outputLink
Exemplo de Uso:
<h:outputLink value="proximo.xhtml">
meuOutputExemplo
</h:outputLink>
Saída HTML:
<a href="proximo.xhtml">
meuOutputExemplo
</a>
16. Componente h:outputText
Exemplo de Uso:
<h:outputText value="meuOutputText"/>
Saída HTML:
meuOutputText
17. Componente h:outputScript
Exemplo de Uso:
<h:outputScript library="scripts" name="main.js"/>
Saída HTML:
<script type="text/javascript" src="/components/faces/javax.faces.resource/main.js">
18. Componente h:outputStylesheet
Exemplo de Uso:
<h:outputStylesheet library="stylesheets" name="main.css"/>
Saída HTML:
<link type="text/css" rel="stylesheet" href ="/components/faces/javax.faces.resource/main.css?ln=stylesheets"/>
19. Componente h:panelGrid
Exemplo de Uso:
<h:panelGrid columns="2">
<h:outputLabel for="inputText1" value="meuLabel1"/>
<h:inputText id="inputText1" value="#{meuBean.inputText}"/>
<h:outputLabel for="inputText2" value="meuLabel2"/>
<h:inputText id="inputText2" value="#{meuBean.inputText}"/>
</h:panelGrid>
Saída HTML:
<table>
<tbody>
<tr>
<td>
<label for="inputText1">meuLabel1</label>
</td>
<td>
<input id="inputText1" type="text" name="inputText1" />
</td>
</tr>
<tr>
<td>
<label for="inputText2">meuLabel2</label>
</td>
<td>
<input id="inputText2" type="text" name="inputText2" />
</td>
</tr>
</tbody>
</table>
20. Componente h:selectBooleanCheckbox
Exemplo de Uso:
<h:selectBooleanCheckbox value="#{meuBean.selectBooleanCheckbox}"/>
Saída HTML:
<input type="checkbox" name="j_idt52" />
21. Componente h:selectOneRadio
Exemplo de Uso:
<h:selectOneRadio value="#{meuBean.selectBooleanCheckbox}">
<f:selectItem itemValue="branco" itemLabel="Branco"/>
<f:selectItem itemValue="preto" itemLabel="Preto"/>
<f:selectItem itemValue="azul" itemLabel="Azul"/>
</h:selectOneRadio>
Saída HTML:
<table>
<tr>
<td>
<input type="radio" checked="checked" name="j_idt54" id="j_idt54:0" value="branco" />
<label for="j_idt54:0">Branco</label>
</td>
<td>
<input type="radio" checked="checked" name="j_idt54" id="j_idt54:1" value="preto" />
<label for="j_idt54:1">Preto</label>
</td>
<td>
<input type="radio" checked="checked" name="j_idt54" id="j_idt54:2" value="azul" />
<label for="j_idt54:2">Azul</label>
</td>
</tr>
</table>
22. Componente h:selectOneListbox
Exemplo de Uso:
<h:selectOneListbox value="#{meuBean.selectBooleanCheckbox}">
<f:selectItem itemValue="branco" itemLabel="Branco"/>
<f:selectItem itemValue="preto" itemLabel="Preto"/>
<f:selectItem itemValue="azul" itemLabel="Azul"/>
</h:selectOneListbox>
Saída HTML:
<select name="j_idt59" size="3">
<option value="branco" selected="selected">Branco</option>
<option value="preto" selected="selected">Preto</option>
<option value="azul" selected="selected">Azul</option>
</select>
23. Componente h:selectOneMenu
Exemplo de Uso:
<h:selectOneMenu value="#{meuBean.selectBooleanCheckbox}">
<f:selectItem itemValue="branco" itemLabel="Branco"/>
<f:selectItem itemValue="preto" itemLabel="Preto"/>
<f:selectItem itemValue="azul" itemLabel="Azul"/>
</h:selectOneMenu>
Saída HTML:
<select name="j_idt64" size="1">
<option value="branco" selected="selected">Branco</option>
<option value="preto" selected="selected">Preto</option>
<option value="azul" selected="selected">Azul</option>
</select>
24. Componente h:selectManyCheckbox
Exemplo de Uso:
<h:selectManyCheckbox value="#{meuBean.selectManyCheckbox}">
<f:selectItem itemValue="branco" itemLabel="Branco"/>
<f:selectItem itemValue="preto" itemLabel="Preto"/>
<f:selectItem itemValue="azul" itemLabel="Azul"/>
</h:selectManyCheckbox>
Saída HTML:
<table>
<tr>
<td>
<input name="j_idt69" id="j_idt69:0" value="branco" type="checkbox" />
<label for="j_idt69:0" class="">Branco</label>
</td>
<td>
<input name="j_idt69" id="j_idt69:1" value="preto" type="checkbox" />
<label for="j_idt69:1" class="">Preto</label>
</td>
<td>
<input name="j_idt69" id="j_idt69:2" value="azul" type="checkbox" />
<label for="j_idt69:2" class="">Azul</label>
</td>
</tr>
</table>
25. Componente h:selectManyListbox
Exemplo de Uso:
<h:selectManyListbox value="#{meuBean.selectBooleanCheckbox}" size="2">
<f:selectItem itemValue="branco" itemLabel="Branco"/>
<f:selectItem itemValue="preto" itemLabel="Preto"/>
<f:selectItem itemValue="azul" itemLabel="Azul"/>
</h:selectManyListbox>
Saída HTML:
<select name="j_idt74" size="2">
<option value="branco" selected="selected">Branco</option>
<option value="preto" selected="selected">Preto</option>
<option value="azul" selected="selected">Azul</option>
</select>
26. Componente h:selectManyMenu
Exemplo de Uso:
<h:selectManyMenu value="#{meuBean.selectBooleanCheckbox}">
<f:selectItem itemValue="branco" itemLabel="Branco"/>
<f:selectItem itemValue="preto" itemLabel="Preto"/>
<f:selectItem itemValue="azul" itemLabel="Azul"/>
</h:selectManyMenu>
Saída HTML:
<select name="j_idt81" multiple="multiple" size="1">
<option value="red">Branco</option>
<option value="green">Preto</option>
<option value="blue">Azul</option>
</select>
<input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-6389029500114305749:3536902027553202703" autocomplete="off" />
27. Outros Componentes
A tag h:inputFile é um novo componente adicionado no JSF 2.2. Este componente permite que um arquivo seja carregado.
Este componente pode ser usado dentro de um h:form e ligado a um bean que tem um campo do tipo javax.servlet.http.Part.
Segue na Listagem 19 um exemplo
Listagem 19. Exemplo de um formulário utilizando h:inputFile.
<h:form enctype="multipart/form-data">
<h:inputFile value="#{fileUploadBean.file}"/><br/>
<h:commandButton value="Carregar Arquivo"/><p/>
</h:form>
Neste código temos um formulário HTML que é criado através do elemento h:form. O formulário deve ter como valor "multipart/form-data" no atributo enctype. Este é um tipo MIME padrão que deveria ser usado para submeter formulários que contém arquivos, dados que não sejam ASCII e dados binários.
No código acima também temos o atributo value que é ligado ao bean. Um exemplo de um bean para o código acima é dado na Listagem 20.
Listagem 20. Exemplo de um bean para carregar o arquivo.
@Named
@RequestScoped
public class FileUploadBean {
private Part file;
public Part getFile() {
return file;
}
public setFile(Part file) {
this.file = file;
}
}
O arquivo será carregado quando o botão “Carregar Arquivo” for clicado.
O upload do arquivo também pode ser empacotado em uma tag .
Segue o exemplo na Listagem 21.
Listagem 21. Exemplo utilizando f:ajax.
<h:form enctype="multipart/form-data" prependId="false">
<h:inputFile value="#{fileUploadBean.file}"/><br/>
<h:commandButton value="Upload">
<f:ajax onevent="statusUpdate"/>
</h:commandButton>
</h:form>
<textarea id="status" cols="40" rows="10" readonly="readonly"/>
Este código é similar ao código mostrado anteriormente com a diferença que o fora do h:form é um espaço reservado para a exibição do status do upload do arquivo. Além disso, o h:commandButton tem uma tag aninhada. Esta tag invoca o método statusUpdate definido separadamente num JavaScript definido, conforme a Listagem 22.
Listagem 22. Exemplo de JavaScript para mostra o status.
<br>var statusUpdate = function statusUpdate(data) {<br> var statusArea = document.getElementById("status");<br> var text = statusArea.value;<br> text = text + "Nome: " + data.source.id;<br> <br> if (data.type === "event") {<br> text = text + " Event: " + data.status + " ";<br> }<br> statusArea.value = text;<br>}<br>
As últimas versões do JSF têm aumentado significativamente a produtividade das equipes de desenvolvimento, tornando mais simples e prático desenvolver em cima dessa plataforma. A última versão do JSF, a versão 2.2 em especial, teve um incremento de funcionalidades e uma atualização significativa em alguns pontos de diversos outros componentes para fornecer uma maior produtividade aos desenvolvedores.
Bibliografia
[1]Josh Juneau. Java EE 7 Recipes: A Problem-Solution Approach. Apress, 2013.
[2]Josh Juneau. Introducing Java EE 7: A Look at What's New. Apress, 2013.
[3]Arun Gupta. Java EE 7 Essentials. O'Reilly, 2013.