--- 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 ~~~ 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. ##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}`