Operações comuns do FreeMarker

Operações comuns do FreeMarker

1. Introdução

O FreeMarker é um mecanismo de modelo, escrito em Java, e mantido pela Apache Foundation. Podemos usar a FreeMarker Template Language, também conhecida como FTL, para gerar muitos formatos baseados em texto, como páginas da Web, email ou arquivos XML.

Neste tutorial, veremos o que podemos fazer pronto para uso com o FreeMarker, embora observe que équite configurablee atéintegrates nicely with Spring.

Vamos começar!

2. Visão geral rápida

Para injetar conteúdo dinâmico em nossas páginas, precisamosuse a syntax that FreeMarker understands:

  • $\{…} no modelo será substituído na saída gerada pelo valor real da expressão dentro das chaves - chamamos isso deinterpolation –. Alguns exemplos são$\{1 + 2} e${variableName}

  • As tags FTL são como tags HTML (mas contêm# ou@) e o FreeMarker as interpreta, por exemplo<#if…></#if>

  • Os comentários no FreeMarker começam com<#–e terminam com–>

3. A tag Incluir

A diretiva FTLinclude é uma forma de seguirmos o princípio DRY em nossa aplicação. Definiremos o conteúdo repetitivo em um arquivo e o reutilizaremos em diferentes modelos do FreeMarker com uma única taginclude.

Um desses casos de uso é quando queremos incluir a seção do menu em muitas páginas. Primeiro, definiremos a seção do menu dentro de um arquivo - vamos chamá-la demenu.ftl - com o seguinte conteúdo:

E em nossa página HTML, vamos incluir omenu.ftl criado:




<#include 'fragments/menu.ftl'>
    
Dashboard page

E também podemos incluir FTL em nossos fragmentos, o que é ótimo.

4. Tratamento da Existência de Valor

FTL considerará qualquer valornull como um valor ausente. Portanto, precisamos ser extremamente cuidadosos eadd logic to handle null dentro do nosso modelo.

Podemos usar o operador?? para verificar se existe um atributo, ou propriedade aninhada. O resultado é um booleano:

${attribute??}

Então, nós testamos o atributo paranull,, mas isso nem sempre é suficiente. Vamos agora definir um valor padrão como um substituto para esse valor ausente. Para fazer isso, precisamos do operador! colocado após o nome da variável:

${attribute!'default value'}

Usando colchetes, podemos agrupar muitos atributos aninhados.

Por exemplo, para verificar se o atributo existe e tem uma propriedade aninhada com outra propriedade aninhada, envolvemos tudo:

${(attribute.nestedProperty.nestedProperty)??}

Por fim, juntando tudo, podemos incorporá-los ao conteúdo estático:

Testing is student property exists: ${student???c}

Using default value for missing student: ${student!'John Doe'}

Wrapping student nested properties: ${(student.address.street)???c}

E, sestudent fossenull, veríamos:

Testing is student property exists: false

Using default value for missing student: John Doe

Wrapping student nested properties: false

Observethe additional ?c directive used after the ??. Fizemos isso para converter o valor booleano em uma sequência legível por humanos.

5. A tag If-Else

As estruturas de controle estão presentes no FreeMarker, e o tradicional if-else é provavelmente familiar:

<#if condition>
    
<#elseif condition2>
    
<#elseif condition3>
    
<#else>
    

Embora os ramoselseifeelse sejam opcionais, as condições devem ser resolvidas para um valor booleano.

Para nos ajudar com nossas avaliações, provavelmente usaremos um dos seguintes:

  • x == y para verificar éx é igual ay

  • x != y para retornartrue apenas sex for diferente dey

  • x lt y significa quex deve ser estritamente menor quey - também podemos usar< em vez delt

  • x gt y avalia paratrue apenas sex for estritamente maior quey - podemos usar> em vez degt

  • x lte y testa sex é menor ou igual ay - a alternativa paralte é

  • x gte y testa sex é maior ou igual ay - a alternativa degte é> =

  • x?? para verificar a existência dex

  • sequence?seqContains(x) valida a existência dex dentro de uma sequência

É muito importante ter em mente queFreeMarker considers >= and > as closing characters for an FTL tag. A solução é envolver seu uso entre parênteses ouuse gte or gt instead.

Juntando-o, para o seguinte modelo:

<#if status??>
    

${status.reason}

<#else>

Missing status!

Terminamos com o código HTML resultante:

 

404 Not Found

Missing status!

6. Recipientes de subvariáveis

No FreeMarker, temos três tipos de contêineres para subvariáveis:

  • Hashes são uma sequência de pares de valores-chave - a chave deve ser única dentro do hash e não temos uma ordem

  • Sequences são listas onde temos um índice associado a cada valor - um fato digno de nota é que as subvariáveis ​​podem ser de diferentes tipos

  • Collections são um caso especial de sequências onde não podemos acessar o tamanho ou recuperar valores por índice - ainda podemos iterá-los com a taglist!

6.1. Iterando itens

Podemos iterar sobre um contêiner de duas maneiras básicas. O primeiro é onde iteramos sobre cada valor e temos lógica acontecendo para cada um deles:

<#list sequence as item>
    

Ou, quando queremos iterarHash, acessando a chave e o valor:

<#list hash as key, value>
    

A segunda forma é mais poderosa porque também nos permite definir a lógica que deve ocorrer em várias etapas da iteração:

<#list sequence>
    
    <#items as item>
        
    
    
<#else>
    

Oitem representa o nome da variável em loop, mas podemos renomeá-la como quisermos. O branchelse é opcional.

Para um exemplo prático, defina bem um modelo em que listamos alguns status:

<#list statuses>
    
    <#items as status>
  • ${status}
<#else>

No statuses available

Isso nos retornará o seguinte HTML quando nosso contêiner for[“200 OK”, “404 Not Found”, “500 Internal Server Error”]:

  • 200 OK
  • 404 Not Found
  • 500 Internal Server Error

6.2. Manuseio de itens

Um hash nos permite duas funções simples:keys para recuperar apenas as chaves contidas evalues para recuperar apenas os valores.

Uma sequência é mais complexa; podemos agrupar as funções mais úteis:

  • chunkejoin para obter uma sub-sequência ou combinar duas sequências

  • reverse,sort, esortBy para modificar a ordem dos elementos

  • first elast irá recuperar o primeiro ou último elemento, respectivamente

  • size representa o número de elementos na sequência

  • seqContains,seqIndexOf ouseqLastIndexOf para procurar um elemento

7. Manuseio de tipo

FreeMarker vem coma huge variety of functions (built-ins) disponíveis para trabalhar com objetos. Vamos ver algumas funções usadas com frequência.

7.1. Manuseio de String

  • url eurlPath vão escapar do URL da string, com a exceção de queurlPath não vai escapar da barra /

  • jString,jsString,ejsonString aplicarão as regras de escape para Java, Javascript e JSON, respectivamente

  • capFirst,uncapFirst,upperCase,lowerCaseecapitalize são úteis para mudar o caso de nossa string, conforme implícito em seus nomes

  • boolean,date,time,datetime enumber são funções para converter de uma string para outros tipos

Agora vamos usar algumas dessas funções:

${'http://myurl.com/?search=Hello World'?urlPath}

${'Using " in text'?jsString}

${'my value?upperCase}

${'2019-01-12'?date('yyyy-MM-dd')}

E a saída para o modelo acima será:

http%3A//myurl.com/%3Fsearch%3DHello%20World

MY VALUE

Using \" in text

12.01.2019

Ao usar a funçãodate, também passamos o padrão a ser usado para analisar o objeto String. FreeMarker uses the local format unless specified otherwise, por exemplo, na funçãostring disponível para objetos de data.

7.2. Manipulação de números

  • round,floor eceiling podem ajudar com números de arredondamento

  • abs retornará o valor absoluto de um número

  • string irá converter o número em uma string. Também podemos passar quatro formatos de número predefinidos:computer,currency,number oupercent ou definir nosso próprio formato, como[ “0.#” ]

Vamos fazer uma cadeia de algumas operações matemáticas:

${(7.3?round + 3.4?ceiling + 0.1234)?string('0.##')}

E como esperado, o valor resultante é11.12.

7.3. Tratamento de datas

  • .now representa a data-hora atual

  • date,time edatetime podem retornar as seções de data e hora do objeto data-hora

  • string irá converter data-hora em strings - também podemos passar o formato desejado ou usar um pré-definido

Agora vamos obter a hora atual e formatar a saída em uma string contendo apenas as horas e minutos:

${.now?time?string('HH:mm')}

O HTML resultante será:

15:39

8. Manipulação de exceção

Veremos duas maneiras de lidar com exceções para um modelo FreeMarker.

A primeira forma é usar tagsattempt-recover para definir o que devemos tentar executar e um bloco de código que deve ser executado em caso de erro.

A sintaxe é:

<#attempt>
    
<#recover>
    

Ambas as tagsattempt erecover são obrigatórias. In case of an error, it rolls back the attempted block and will execute only the code in the recover section.

Mantendo essa sintaxe em mente, vamos definir nosso modelo como:

Preparing to evaluate

<#attempt>

Attribute is ${attributeWithPossibleValue??}

<#recover>

Attribute is missing

Done with the evaluation

QuandoattributeWithPossibleValue estiver faltando, veremos:

Preparing to evaluate

Attribute is missing

Done with the evaluation

E a saída quandoattributeWithPossibleValue existe é:

Preparing to evaluate

Attribute is 200 OK

Done with the evaluation

A segunda maneira é configurar o FreeMarker o que deve acontecer em caso de exceções.

Com o Spring Boot, configuramos isso facilmente através do arquivo de propriedades; Aqui estão algumas configurações disponíveis:

  • spring.freemarker.setting.template_exception_handler=rethrow relança a exceção

  • spring.freemarker.setting.template_exception_handler=debug envia as informações de rastreamento de pilha para o cliente e, em seguida, lança novamente a exceção.

  • spring.freemarker.setting.template_exception_handler=html_debug envia as informações de rastreamento de pilha para o cliente, formatando-as para que sejam geralmente bem legíveis no navegador e, em seguida, re-lança a exceção.

  • spring.freemarker.setting.template_exception_handler=ignore pula as instruções com falha, permitindo que o modelo continue a execução.

  • spring.freemarker.setting.template_exception_handler=default

9. Métodos de chamada

Às vezes, queremos chamar métodos Java de nossos modelos FreeMarker. Agora veremos como fazer isso.

9.1. Membros estáticos

Para começar a acessar membros estáticos, podemos atualizar nossa configuração FreeMarker global ou adicionar um atributo de tipo StaticModels no modelo, sob o nome de atributostatics:

model.addAttribute("statics", new DefaultObjectWrapperBuilder(new Version("2.3.28"))
    .build().getStaticModels());

O acesso a elementos estáticos é direto.

Primeiro, importamos os elementos estáticos de nossa classe usando a tag assign, depois decidimos um nome e, finalmente, o caminho de classe Java.

Veja como vamos importar a classeMath em nosso modelo, mostrar o valor do campoPI estático e usar o métodopow estático:

<#assign MathUtils=statics['java.lang.Math']>

PI value: ${MathUtils.PI}

2*10 is: ${MathUtils.pow(2, 10)}

O HTML resultante é:

PI value: 3.142

2*10 is: 1,024

9.2. Membros Bean

Os membros do Bean são muito fáceis de acessar:use the dot (.)e é isso!

Para o nosso próximo exemplo, vamos adicionar um objetoRandom ao nosso modelo:

model.addAttribute("random", new Random());

No nosso modelo FreeMarker, vamos gerar um número aleatório:

Random value: ${random.nextInt()}

Isso causará uma saída semelhante a:

Random value: 1,329,970,768

9.3. Métodos personalizados

A primeira etapa para adicionar um método personalizado é ter uma classe que implemente a interfaceTemplateMethodModelEx do FreeMarker e defina nossa lógica dentro do métodoexec:

public class LastCharMethod implements TemplateMethodModelEx {
    public Object exec(List arguments) throws TemplateModelException {
        if (arguments.size() != 1 || StringUtils.isEmpty(arguments.get(0)))
            throw new TemplateModelException("Wrong arguments!");
        String argument = arguments.get(0).toString();
        return argument.charAt(argument.length() - 1);
    }
}

Adicionaremos uma instância da nossa nova classe como um atributo no modelo:

model.addAttribute("lastChar", new LastCharMethod());

O próximo passo é usar nosso novo método dentro do nosso modelo:

Last char example: ${lastChar('mystring')}

Finalmente, a saída resultante é:

Last char example: g

10. Conclusão

Neste artigo, vimos como usar o mecanismo de modelo FreeMarker dentro de nosso projeto. Focamos em operações comuns, em como manipular objetos diferentes e em alguns tópicos mais avançados.

A implementação de todos esses snippets está disponívelover on GitHub.