Home > JSF, JavaEE > URLs amigáveis no JSF 2.0

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  :D

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.

Gilliard Cordeiro JSF, JavaEE , , ,

  1. Ezequiel de Witt
    May 29th, 2009 at 18:58 | #1

    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

  2. June 17th, 2009 at 16:43 | #2

    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.

  3. Emanuel
    December 25th, 2009 at 10:01 | #3

    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?

  4. January 7th, 2010 at 08:56 | #4

    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.

  5. Léo
    March 17th, 2010 at 09:48 | #5

    Olá Gilliard,
    muito com seu blog cara!

    Sucesso pra vc.

  1. No trackbacks yet.