---
title: Trabalhando com a View
---
#Compartilhando objetos com a view
Para registrar objetos a serem acessados na view, usamos o método include:
~~~
#!java
@Controller
class ClientController {
@Inject
private Result result;
public void busca(int id) {
result.include("mensagem", "Alguma mensagem");
result.include("cliente", new Cliente(id));
}
}
~~~
Agora as variáveis `mensagem` e `cliente` estão disponíveis para uso em seu template engine.
É possível registrar o objeto por meio da invocação do método include com um único argumento:
~~~
#!java
@Controller
class ClientController {
@Inject
private Result result;
public void busca(int id) {
result.include("Alguma mensagem").include(new Cliente(id));
}
}
~~~
Nesse caso, a primeira invocação registra a chave `string` e a segunda, a chave `cliente`. Você pode alterar o comportamento de convenção de chaves no seu próprio `TypeNameExtractor`.
##Custom PathResolver
Por padrão, para renderizar suas views, o VRaptor segue a convenção:
~~~
#!java
public class ClientsController {
public void list() {
//...
}
}
~~~
Este método acima renderizará a view `/WEB-INF/jsp/clients/list.jsp`. No entanto, nem sempre queremos esse comportamento, e precisamos usar algum template engine, como por exemplo, Freemarker ou Velocity, e precisamos mudar essa convenção. Um jeito fácil de mudar essa convenção é estendendo a classe `DefaultPathResolver`:
~~~
#!java
@Specializes
public class FreemarkerPathResolver extends DefaultPathResolver {
protected String getPrefix() {
return "/WEB-INF/freemarker/";
}
protected String getExtension() {
return "ftl";
}
}
~~~
Desse jeito, a lógica irá renderizar a view `/WEB-INF/freemarker/clients/list.ftl`. Se, ainda assim, isso não for o suficiente, você pode implementar a interface `PathResolver` e fazer qualquer convenção que você queira, não esquecendo de anotar a classe com `@Specializes`.
##View
Se você quiser mudar a view de alguma lógica específica, você pode usar o objeto `Result`:
~~~
#!java
@Controller
public class ClientsController {
@Inject
private Result result;
public void list() { ... }
public void save(Client client) {
//...
result.use(Results.logic()).redirectTo(ClientsController.class).list();
}
}
~~~
Por padrão, existem estes tipos de views implementadas:
| Results.logic() | que vai redirecionar para uma outra lógica qualquer do sistema
| Results.page() | que vai redirecionar diretamente para uma página, podendo ser um JSP, um HTML, ou qualquer URI relativa ao web application dir, ou ao contexto da aplicação.
| Results.http() | que manda informações do protocolo HTTP como status codes e headers.
| Results.status() | manda status codes com mais informações.
| Results.referer() | que usa o header Referer para fazer redirects ou forwards.
| Results.nothing() | apenas retorna o código de sucesso (HTTP 200 OK).
| Results.xml() | serializa objetos em XML.
| Results.json() | serializa objetos em JSON.
| Results.representation() | serializa objetos em um formato determinado pela requisição (parâmetro _format ou header Accept)
{: .content-table}
##Atalhos no Result
Alguns redirecionamentos são bastante utilizados, então foram criados atalhos para eles. Os atalhos disponíveis são:
| result.forwardTo("/some/uri") | result.use(page()).forward("/some/uri");
| result.redirectTo("/some/uri") | result.use(page()).redirect("/some/uri)
| result.permanentlyRedirectTo("/some/uri") | result.use(status()).movedPermanentlyTo("/some/uri");
| result.forwardTo(ClientController.class).list() | result.use(logic()).forwardTo(ClientController.class).list();
| result.redirectTo(ClientController.class).list() | result.use(logic()).redirectTo(ClientController.class).list();
| result.of(ClientController.class).list() | result.use(page()).of(ClientController.class).list();
| result.permanentlyRedirectTo(Controller.class) | use(status()).movedPermanentlyTo(Controller.class);
| result.notFound() | use(status()).notFound()
| result.nothing() | use(nothing());
{: .content-table}
Além disso, se o redirecionamento é para um método do mesmo Controller, podemos usar:
| result.forwardTo(this).list() | result.use(logic()).forwardTo(this.getClass()).list();
| result.redirectTo(this).list() | result.use(logic()).redirectTo(this.getClass()).list();
| result.of(this).list() | result.use(page()).of(this.getClass()).list();
| result.permanentlyRedirectTo(this) | use(status()).movedPermanentlyTo(this.getClass());
{: .content-table}
##Redirecionamento e forward
No VRaptor, podemos tanto realizar um redirect ou um forward do usuário para uma outra lógica ou um JSP. Apesar de serem conceitos da API de Servlets, vale a pena relembrar a diferença: o redirecionamento acontece no lado do cliente, através de códigos HTTP que farão o browser acessar uma nova URL; já o forward acontece no lado do servidor, totalmente transparente para o cliente/browser.
Um bom exemplo de uso do redirect é no chamado 'redirect-after-post'. Por exemplo: quando você adiciona um cliente e que, após o formulário submetido, o cliente seja retornado para a página de listagem de clientes. Fazendo isso com redirect, impedimos que o usuário atualize a página (F5) e reenvie toda a requisição, acarretando em dados duplicados.
No caso do forward, um exemplo de uso é quando você possui uma validação e essa validação falhou, geralmente você quer que o usuário continue na mesma tela do formulário com os dados da requisição preenchidos, mas internamente você vai fazer o forward para outra lógica de negócios (a que prepara os dados necessários para o formulário).
##Escopo Flash automático
Se você adicionar objetos no `Result` e fizer um Redirect, esses objetos estarão disponíveis na próxima requisição.
~~~
#!java
public void adiciona(Cliente cliente) {
dao.adiciona(cliente);
result.include("mensagem", "Cliente adicionado com sucesso");
result.redirectTo(ClientesController.class).lista();
}
~~~
lista.jsp:
~~~
#!xml
${mensagem}
~~~
##Accepts e o parâmetro _format
Muitas vezes, precisamos renderizar formatos diferentes para uma mesma lógica. Por exemplo queremos retornar um JSON, em vez de um HTML. Para fazer isso, podemos definir o Header Accepts da requisição para que aceite o tipo desejado, ou colocar um parâmetro `_format` na requisição.
Se o formato for JSON, a view renderizada por padrão será: `/WEB-INF/jsp/{controller}/{logic}.json.jsp`, ou seja, em geral será renderizada a view: `/WEB-INF/jsp/{controller}/{logic}.{formato}.jsp`. Se o formato for HTML você não precisa colocá-lo no nome do arquivo. O parâmetro `_format` tem prioridade sobre o header Accepts.
##Ajax: construindo na view
Para devolver um JSON na sua view, basta que sua lógica disponibilize o objeto para a view, e dentro da view você forme o JSON como desejar. Como no exemplo, o seu `/WEB-INF/jsp/clients/load.json.jsp`:
~~~
#!javascript
{ nome: '${client.name}', id: '${client.id}' }
~~~
E na lógica:
~~~
#!java
@Controller
public class ClientsController {
@Inject
private Result result;
@Inject
private ClientDao dao;
public void load(Client client) {
result.include("client", dao.load(client));
}
}
~~~
##Ajax: Versão programática
Se você quiser que o VRaptor serialize automaticamente seus objetos para XML ou JSON, você pode escrever em sua lógica:
~~~
#!java
import static br.com.caelum.vraptor.view.Results.*;
@Controller
public class ClientsController {
@Inject
private Result result;
@Inject
private ClientDao dao;
public void loadJson(Cliente cliente) {
result.use(json()).from(cliente).serialize();
}
public void loadXml(Cliente cliente) {
result.use(xml()).from(cliente).serialize();
}
}
~~~
Os resultados vão ser parecidos com:
~~~
#!javascript
{"cliente": {
"nome": "Joao"
}}
~~~
e em xml:
~~~
#!xml
Joao
~~~
Por padrão, apenas campos de tipos primitivos serão serializados (String, números, enums, datas), se você quiser incluir um campo de tipo não primitivo você precisa incluí-lo explicitamente:
~~~
#!java
result.use(json()).from(cliente).include("endereco").serialize();
~~~
Vai resultar em algo parecido com:
~~~
#!javascript
{"cliente": {
"nome": "Joao",
"endereco" {
"rua": "Vergueiro"
}
}}
~~~
Você pode também excluir alguns da serialização:
~~~
#!java
result.use(json()).from(usuario).exclude("senha").serialize();
~~~
Vai resultar em algo parecido com:
~~~
#!javascript
{"usuario": {
"nome": "Joao",
"login": "joao"
}}
~~~
Você também pode excluir ou incluir vários campos.
~~~
#!java
result.use(json()).from(usuario).recursive().serialize(); // inclui todos os campos recursivamente
result.use(xml()).from(usuario).exclude("email").serialize(); // excluir o campo email
result.use(xml()).from(usuario).excludeAll().serialize(); // exclui todos os campos
~~~
Por padrão atributos com valores `null` não são serializados no json, é possível modificar este comportamento chamando o método `serializeNulls`:
~~~
#!java
result.use(json()).serializeNulls().from(new Pessoa("João", null)).serialize();
~~~
O JSON resultante será o seguinte:
~~~
#!javascript
{"pessoa":{
"nome": "João",
"idade": null
}}
~~~
Você pode adicionar versionamento aos seus resultados baseados em JSON.
Através desse recurso é possível versionar uma API.
Para exemplificar a utilidade do versionamento, suponha que foi criada uma API que retorna clientes de um determinado sistema.
A primeira versão, retorna clientes definidos através da seguinte estrutura:
~~~
#!java
public class Cliente {
private String nome;
private String ramo;
// getters e setters omitidos
}
~~~
Agora suponha que você criou um método no seu controlador que possui o seguinte resultado:
~~~
#!java
@Get{"/clientes"}
public void clientes() {
List clientes = repositorioDeClientes.getAll();
result.use(json()).from(clientes).serialize();
}
~~~
Até agora não há problema algum. Porém, caso seja necessário disponibilizar um novo atributo como resultado que só fará sentido para uma nova versão da API, como poderíamos contornar a situação? Poderiamos criar uma segunda classe chamada ClienteV2 com o novo atributo e o método do controlador que retorna os clientes poderia ser reescrito conforme abaixo:
~~~
#!java
@Get{"/{versaoParam}/clientes"}
public void clientes(String versaoParam) {
double versao = Double.parseDouble(versaoParam);
if (versao == 2.0) {
result.use(json()).from(repositorioDeClientes.getAll()).serialize();
} else {
result.use(json()).from(repositorioDeClientes.getAllFromV2()).serialize();
}
}
~~~
Certamente a solução acima não é nada boa. Como o VRaptor utiliza a biblioteca GSON (criada pelo Google), podemos utilizar a mesma classe Cliente utilizando a anotação @Since nos atributos que queremos versionar. Essa anotação indica que um determinado atributo está disponível apartir de uma versão *x* conforme o exemplo abaixo:
~~~
#!java
public class Cliente {
private String nome;
private String ramo;
@Since(2.0)
private Calendar dataDeRegistro;
// getters e setters omitidos
}
~~~
Dessa forma o método do controlador agora pode ser reescrito da seguinte forma:
~~~
#!java
@Get{"/{versaoParam}/clientes"}
public void clientes(String versaoParam) {
double versao = Double.parseDouble(versaoParam);
List clientes = repositorioDeClientes.getAll();
result.use(json()).version(versao).from(clientes).serialize();
}
~~~
Pronto, agora os clientes de nossa API, podem solicitar um resultado com uma versão específica.
A implementação para serialização de XML padrão é baseada no XStream, então é possível configurar a serialização por anotações ou configurações diretas ao XStream, bastando criar a classe:
~~~
#!java
@Specializes
public class CustomXStreamBuilder extends XStreamBuilderImpl {
@Override
protected XStream xmlInstance() {
XStream xStream = super.xmlInstance();
//suas configurações ao XStream aqui
return xStream;
}
}
~~~
Para configurar a serialização com JSON você pode criar uma classe que estenda de `GsonJSONSerialization` e sobrescrever o método `getSerializer()` com as suas configurações.
Na serialização JSON você pode utilizar a anotação `SkipSerialization` para omitir a serialização de um campo ou de uma classe.
A classe abaixo foi anotada com `SkipSerialization`, portanto os seus campos nunca serão serializados para JSON:
~~~
#!java
@SkipSerialization
public class UserPrivateInfo {
private String cpf;
private String telefone;
private String endereco;
// getters e setters
}
~~~
Nesta outra classe, o campo senha não será serializado. O campo info também não será serializado pois a classe `UserPrivateInfo` foi anotado com `SkipSerialization`:
~~~
#!java
public class User {
private String nome;
private String login;
@SkipSerialization
private String senha;
private UserPrivateInfo info;
// getters e setters
}
~~~
Realizando a seguinte chamada:
~~~
#!java
User user = ...;
result.use(json()).withoutRoot().from(user).recursive().serialize();
~~~
O resultado obtido é semelhante à:
~~~
#!javascript
{
"nome": "Joao",
"login": "joao"
}
~~~
Perceba que o campo senha e todo o `UserPrivateInfo` não foram serializados.
##Serializando Collections##
Ao serializar coleções, o padrão é colocar a tag `list` em volta dos elementos:
~~~
#!java
List clientes = ...;
result.use(json()).from(clientes).serialize();
//ou
result.use(xml()).from(clientes).serialize();
~~~
Vai resultar em algo como:
~~~
#!javascript
{"list": [
{ "nome": "Joao" },
{ "nome": "Maria" }
]}
~~~
ou
~~~
#!xml
Joao
Maria
~~~
É possível personalizar o elemento de fora usando o método:
~~~
#!java
List clientes = ...;
result.use(json()).from(clientes, "clientes").serialize();
//ou
result.use(xml()).from(clientes, "clientes").serialize();
~~~
Vai resultar em algo como:
~~~
#!javascript
{"clientes": [
{"nome": "Joao"},
{"nome": "Maria"}
]}
~~~
ou
~~~
#!xml
Joao
Maria
~~~
Os includes e excludes funcionam como se você os estivesse aplicando num elemento de dentro da lista. Por exemplo se você quiser incluir o endereço no cliente:
~~~
#!java
List clientes = ...;
result.use(json()).from(clientes).include("endereco").serialize();
~~~
Com resultado:
~~~
#!javascript
{"list": [
{
"nome": "Joao",
"endereco": { "rua": "Vergueiro, 3185" }
},
{
"nome": "Maria",
"endereco": { "rua": "Vergueiro, 3185" }
}
]}
~~~
##Serializando JSON sem elemento raiz
Se você quiser serializar um objeto em JSON sem dar nomes a eles, pode fazer isso com o método withoutRoot:
~~~
#!java
result.use(json()).from(carro).serialize(); //=> {'carro': {'cor': 'azul'}}
result.use(json()).withoutRoot().from(carro).serialize(); //=> {'cor': 'azul'}
~~~
##Indentação do resultado JSON e XML
Por padrão, o resultado da serialização é sem indentação, o que pode ser útil para melhorar o tempo de download do browser. Porém se você precisar exibir o resultado do JSON ou XML com indentação, você pode usar o método `indented`.
~~~
#!java
result.use(json()).indented().from(carro).serialize();
~~~
Dessa forma o resultado seria:
~~~
#!javascript
{
'carro':
{
'cor': 'azul'
}
}
~~~
Ou você pode também usar o Environment para definir a indentação padrão conforme o ambiente. Neste caso você pode definir que no ambiente `DEVELOPMENT` a indentação será ativada, e no ambiente `PRODUCTION` será desativada.
`development.properties`:
~~~
#!properties
br.com.caelum.vraptor.serialization.xml.indented=true
br.com.caelum.vraptor.serialization.json.indented=true
~~~
`production.properties`:
~~~
#!properties
br.com.caelum.vraptor.serialization.xml.indented=false
br.com.caelum.vraptor.serialization.json.indented=false
~~~
**Nota:** A configuração do `Environment` possui prioridade menor que no código. Ou seja, se você definir `br.com.caelum.vraptor.serialization.xml.indented=false` no `Environment` e chamar o método `indented` no seu código, o resultado será um JSON ou XML indentado.
## Criando links na view com o linkTo
LinkTo é uma funcionalidade que permite você criar links nas páginas JSP sem precisar escrever o link, mas sim indicando a classe do controller, e o método.
Para o seguinte controller:
~~~
#!java
public class ClientController {
@Path("/client/list")
public void list() {
// alguma lógica aqui
}
@Path("/client/{id}")
public void show(Long id) {
// alguma lógica aqui
}
}
~~~
Para chamar os métodos `list` e `show` respectivamente, teriamos que escrever no JSP:
~~~
#!jsp
listar clientes
ver cliente 1
~~~
Usando a funcionalidade do linkTo, podemos simplicar a chamada para o método `list`:
~~~
#!jsp
listar clientes
~~~
Como o método não possui parâmetros, você também pode omitir os parênteses.
~~~
#!jsp
listar clientes
~~~
Para chamar o método `show`, podemos fazer a chamada desta forma:
~~~
#!jsp
ver cliente 1
~~~
Ao invés do parâmetro fixo, você pode passar variáveis do JSTL, como por exemplo:
~~~
#!jsp
ver cliente ${client.name}
~~~
##Inclusão automática de parâmetros
Você pode anotar seu método com `@IncludeParameters` para que todos os parâmetros de um método do seu controller sejam inclusos automaticamente na `view`.
Então, no lugar de fazer algo como:
~~~
#!java
public void filtra(Intervalo intervalo, Representante representante,
Unidade unidade, BigDecimal valorMaximo, BigDecimal valorMin) {
// lógica de filtro
result.include("intervalo", intervalo);
result.include("representante", representante);
result.include("valorMaximo", valorMaximo);
result.include("valorMin", valorMin);
}
~~~
Agora você pode fazer:
~~~
#!java
@IncludeParameters
public void filtra(Intervalo intervalo, Representante representante,
Unidade unidade, BigDecimal valorMaximo, BigDecimal valorMin) {
// lógica de filtro
}
~~~
Todos os parâmetros estarão disponiveis pela jsp com EL: `${intervalo.inicio}`