URLs amigáveis no JSF 2.0
Hoje vou falar de mais uma novidade do JSF 2, cuja falta era motivo de muita reclamação: URLs amigávies, bookmarking, método GET e outros nomes que podemos dar. O suporte a essa nova funcionalidade é dado por dois pares de componentes, de um lado h:button e h:link e do outro f:metadata e f:viewParam.
Os componentes h:button e h:link servem para originar as ações jsf assim como os componentes h:command{Button|Link}, porém usando GET em vez de POST. Esses componentes possuem um atributo chamado outcome, que representa a regra de navegação do JSF, assim como seria colocar uma String diretamente na action do h:command{Button|Link}. Para passar parâmetros colocamos a tag f:param dentro do h:link ou h:button.
Como exemplo vamos ver uma aplicaçãozinha que tem uma tela de listagem e outra de visualização de Pessoas. A seguir um trecho da página de listagem de pessoas, listarPessoas.xhtml:
<h:form> <h:dataTable value="#{pessoaController.pessoas}" var="pessoa"> <h:column> #{pessoa.nome} </h:column> <h:column> <h:link outcome="/verPessoa" value="Editar"> <f:param name="pessoa" value="#{pessoa.nome}"/> </h:link> </h:column> </h:dataTable> </h:form>
Nesse exemplo, no outcome eu já estou usando o esquema novo de navegação comentado no post passado. O link acima vai gerar uma url parecida com http://localhost:8080/exemplojsf2/verPessoa.jsf?pessoa=fulano_0.
Agora do lado da página que recebe a requisição temos os componentes f:metadata e f:viewParam. A primeira é apenas uma tag que engloba as f:viewParam. Já as tags f:viewParam se comportam de forma muito parecida com um h:inputText, podemos dizer que praticamente a única diferença é no input a gente digita em um formulário, enquanto na f:viewParam escrevemos na URL.
A tag f:viewParam possue os seguintes atributos: converter, converterMessage, required, requiredMessage, validator, validatorMessage, value, valueChangeListener, maxlength e for (este último voltado para o novo esquema de component composition do Facelets). Como podemos ver, é praticamente um h:inputText.
Vamos ver então um trecho do código da página que recebe a requisição, verPessoa.xhtml:
<f:view> <f:metadata> <f:viewParam name="pessoa" value="#{pessoaController.pessoaSelecionada}" /> </f:metadata> <h:head> <title>Ver Pessoa</title> </h:head> <h:body> <h:form> Nome: #{pessoaController.pessoaSelecionada.nome} </h:form> </h:body> </f:view>
Não precisei colocar um converter no f:viewParam pois configurei um converter “forClass” como veremos mais a frente.
Agora a nossa classe
public class Pessoa { private String nome; public Pessoa() { } public Pessoa(String nome) { this.nome = nome; } //getter e setter suprimido }
Isso mesmo, a classe é complexa desse jeito
Como o intuito é só um exemplo, eu nem me preocupei com banco de dados ou algum mecanismo mais interessante, apenas criei um conversor para mostrar que o novo esquema não permite apenas o uso de Strings. Segue o código do conversor:
@FacesConverter(forClass=Pessoa.class) public class PessoaConverter implements Converter { @Override public Object getAsObject(FacesContext context, UIComponent component, String string) { return new Pessoa(string); } @Override public String getAsString(FacesContext context, UIComponent component, Object object) { return ((Pessoa)object).getNome(); } }
E agora nosso managed bean que recebe não uma String, e sim objetos do nosso domínio. Eu falo isso o tempo todo pois preciso deixar isso bem claro, senão eu fico doido de ver uma aplicação usando JSF passando String e Integer de um lado pro outro
Mas tudo bem, deixando o desabafo pra lá vamos ao código:
@ManagedBean(name="pessoaController") @RequestScoped public class PessoaController { private Pessoa pessoaSelecionada; private List<pessoa> pessoas; @PostConstruct public void init() { pessoas = new ArrayList<pessoa>(); for (int i = 0; i < 10; i++) { pessoas.add(new Pessoa("fulano_" + i)); } } //getters e setters suprimidos }
Acredito que por hoje seja suficiente. Vou ver se em breve escrevo algo mais específico sobre facelets.
Olá Gilliard, tenho acompanhando seu blog com frequência, é a principal referência em português de JSF2. Parabéns.
Seguinte, estou estudando um pouco JSf 2.0 e me surgiu uma dúvida, com faço pra saber a linha que está sendo mostrada utilizando h:datatable ou o ui:repeat, como tem o varStatus.count no jstl c:forEach?
grato,Ezequiel
Desculpa pela demora na resposta, mas é que eu passei por uns probleminhas de manutenção no site. Realmente a falta desse atributo é algo negativo. No meu caso, como utilizo o RichFaces, no componente rich:dataTable tem um atributo chamado rowKeyVar que faz o mesmo papel.
Eu sou novo com JSF, mas no exemplo:
Digamos que Pessoa tenha mais de um atributo String, como ficaria…?
A classe PessoaConverter teria um método para cada um?
Emanuel, desculpa a demora em responder, mas é que eu estava de férias (off-line).
Eu dei um exemplo propositalmente simples, mas caso fosse um objeto “real”, você precisaria passar como referência algo que fosse suficiente para você recuperar o objeto. Na prática acaba sendo a chave primária ou alguma coisa que via conta te permita chegar na mesma (caso de segurança por exemplo).
Dê uma olhada no mecanismo de conversão do JSF, o mesmo usado nos selects, pois é a mesma idéia.