Modelo com guidão
1. Visão geral
Neste tutorial, examinaremos a bibliotecaHandlebars.java para facilitar o gerenciamento de modelos.
2. Dependências do Maven
Vamos começar adicionando a dependênciahandlebars:
com.github.jknack
handlebars
4.1.2
3. Um modelo simples
Um modelo de guidão pode ser qualquer tipo de arquivo de texto. It consists of tags like\{{name}} and \{\{#each people}}.
Em seguida, preenchemos essas tags passando um objeto de contexto, como umMap ou outroObject.
3.1. Usandothis
To pass a single String value to our template, we can use any Object as the context. Também devemos usar o sag \ {\ {this}} tem nosso modelo.
Em seguida, o Handlebars chama o métodotoString no objeto de contexto e substitui a tag pelo resultado:
@Test
public void whenThereIsNoTemplateFile_ThenCompilesInline() throws IOException {
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hi {{this}}!");
String templateString = template.apply("example");
assertThat(templateString).isEqualTo("Hi example!");
}
No exemplo acima, primeiro criamos uma instância do ponto de entrada da APIHandlebars, our.
Então, damos a essa instância nosso modelo. Aqui,we just pass the template inline,, mas veremos em um momento algumas maneiras mais poderosas.
Finalmente, damos ao contexto compilado o nosso contexto. \{{this}} acabará chamandotoString,, e é por isso que vemos“Hi example!”.
3.2. Passando aMap como Objeto de Contexto
Acabamos de ver como enviar umString para nosso contexto, agora vamos tentar umMap:
@Test
public void whenParameterMapIsSupplied_thenDisplays() throws IOException {
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hi {{name}}!");
Map parameterMap = new HashMap<>();
parameterMap.put("name", "example");
String templateString = template.apply(parameterMap);
assertThat(templateString).isEqualTo("Hi example!");
}
Semelhante ao exemplo anterior, estamos compilando nosso modelo e, em seguida, passando o objeto de contexto, mas desta vez como umMap.
Além disso, observe que estamos usando\{{name}} em vez de\{{this}}. This means that our map must contain the key, name.
3.3. Passando um objeto personalizado como objeto de contexto
Também podemos passar um objeto personalizado para nosso modelo:
public class Person {
private String name;
private boolean busy;
private Address address = new Address();
private List friends = new ArrayList<>();
public static class Address {
private String street;
}
}
Usando a classePerson, obteremos o mesmo resultado do exemplo anterior:
@Test
public void whenParameterObjectIsSupplied_ThenDisplays() throws IOException {
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hi {{name}}!");
Person person = new Person();
person.setName("example");
String templateString = template.apply(person);
assertThat(templateString).isEqualTo("Hi example!");
}
\{{name}} em nosso modelo irá detalhar nosso objetoPerson e obter o valor dename field_. _
4. Carregadores de modelos
Até agora, usamos modelos que são definidos dentro do código. No entanto, não é a única opção. Também podemosread templates from text files.
Handlebars.java provides special support for reading templates from the classpath, filesystem or servlet context. By default, Handlebars scans the classpath to load the given template:
@Test
public void whenNoLoaderIsGiven_ThenSearchesClasspath() throws IOException {
Handlebars handlebars = new Handlebars();
Template template = handlebars.compile("greeting");
Person person = getPerson("example");
String templateString = template.apply(person);
assertThat(templateString).isEqualTo("Hi example!");
}
So, because we called compile instead of compileInline, esta é uma dica para o Handlebars procurar/greeting.hbs no classpath.
No entanto, também podemos configurar essas propriedades comClassPathTemplateLoader:
@Test
public void whenClasspathTemplateLoaderIsGiven_ThenSearchesClasspathWithPrefixSuffix() throws IOException {
TemplateLoader loader = new ClassPathTemplateLoader("/handlebars", ".html");
Handlebars handlebars = new Handlebars(loader);
Template template = handlebars.compile("greeting");
// ... same as before
}
Nesse caso, estamos dizendo aHandlebars to look for the /handlebars/greeting.html on the classpath.
Finalmente, podemos encadear várias instâncias deTemplateLoader:
@Test
public void whenMultipleLoadersAreGiven_ThenSearchesSequentially() throws IOException {
TemplateLoader firstLoader = new ClassPathTemplateLoader("/handlebars", ".html");
TemplateLoader secondLoader = new ClassPathTemplateLoader("/templates", ".html");
Handlebars handlebars = new Handlebars().with(firstLoader, secondLoader);
// ... same as before
}
Então, aqui, nós temos dois carregadores, e isso significa que o Handlebars irá pesquisar dois diretórios para o modelogreeting.
5. Ajudantes internos
Os auxiliares internos nos fornecem funcionalidade adicional ao escrever nossos modelos.
5.1. with Helper
The with helper changes the current context:
{{#with address}}
I live in {{street}}
{{/with}}
Em nosso modelo de exemplo, a tag\{\{#with address}} inicia a seção e a tag\{\{/with}} termina.
In essence, we’re drilling into the current context object – let’s say person – and setting address as the local context for the with section. Depois disso, cada referência de campo nesta seção será acrescentada porperson.address.
Portanto, a tag\{{street}} manterá o valor deperson.address.street:
@Test
public void whenUsedWith_ThenContextChanges() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
Template template = handlebars.compile("with");
Person person = getPerson("example");
person.getAddress().setStreet("World");
String templateString = template.apply(person);
assertThat(templateString).isEqualTo("\nI live in World
\n");
}
Estamos compilando nosso modelo e atribuindo uma instânciaPerson como o objeto de contexto. Observe que a classePerson tem um campoAddress. Este é o campo que estamos fornecendo ao ajudantewith.
Embora tenhamos avançado um nível em nosso objeto de contexto, é perfeitamente possível ir mais fundo se o objeto de contexto tiver vários níveis aninhados.
5.2. each Helper
The each helper iterates over a collection:
{{#each friends}}
{{name}} is my friend.
{{/each}}
Como resultado de iniciar e fechar a seção de iteração com as tags\{\{#each friends}}e\{\{/each}}, as barras de controle irão iterar sobre o campofriends do objeto de contexto.
@Test
public void whenUsedEach_ThenIterates() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
Template template = handlebars.compile("each");
Person person = getPerson("example");
Person friend1 = getPerson("Java");
Person friend2 = getPerson("Spring");
person.getFriends().add(friend1);
person.getFriends().add(friend2);
String templateString = template.apply(person);
assertThat(templateString).isEqualTo("\nJava is my friend.\n"
+ "\nSpring is my friend.\n");
}
No exemplo, estamos atribuindo duas instânciasPerson ao campofriends do objeto de contexto. Portanto, o guidão repete a parte HTML duas vezes na saída final.
5.3. if Helper
Por fim,the if helper provides conditional rendering.
{{#if busy}}
{{name}} is busy.
{{else}}
{{name}} is not busy.
{{/if}}
Em nosso modelo, fornecemos mensagens diferentes de acordo com o campobusy.
@Test
public void whenUsedIf_ThenPutsCondition() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
Template template = handlebars.compile("if");
Person person = getPerson("example");
person.setBusy(true);
String templateString = template.apply(person);
assertThat(templateString).isEqualTo("\nexample is busy.
\n");
}
Depois de compilar o modelo, estamos definindo o objeto de contexto. Since the busy field is true, the final output becomes <h4>example is busy.</h4>.
6. Auxiliares de modelos personalizados
Também podemos criar nossos próprios auxiliares personalizados.
6.1. Helper
A interfaceHelper nos permite criar um assistente de modelo.
Como primeira etapa, devemos fornecer uma implementação deHelper:
new Helper() {
@Override
public Object apply(Person context, Options options) throws IOException {
String busyString = context.isBusy() ? "busy" : "available";
return context.getName() + " - " + busyString;
}
}
Como podemos ver, a interfaceHelper possui apenas um método que aceita os objetoscontexteoptions. Para nossos propósitos, iremos gerar os camposname ebusy dePerson.
After creating the helper, we must also register our custom helper with Handlebars:
@Test
public void whenHelperIsCreated_ThenCanRegister() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
handlebars.registerHelper("isBusy", new Helper() {
@Override
public Object apply(Person context, Options options) throws IOException {
String busyString = context.isBusy() ? "busy" : "available";
return context.getName() + " - " + busyString;
}
});
// implementation details
}
Em nosso exemplo, estamos registrando nosso ajudante com o nome deisBusy usando o métodoHandlebars.registerHelper().
As the last step, we must define a tag in our template using the name of the helper:
{{#isBusy this}}{{/isBusy}}
Observe que cada auxiliar tem uma tag inicial e final.
6.2. Métodos auxiliares
When we use the Helper interface, we can only create only one helper. In contrast, a helper source class enables us to define multiple template helpers.
Além disso, não precisamos implementar nenhuma interface específica. Nós apenas escrevemos nossos métodos auxiliares em uma classe e o HandleBars extrai as definições auxiliares usando reflexão:
public class HelperSource {
public String isBusy(Person context) {
String busyString = context.isBusy() ? "busy" : "available";
return context.getName() + " - " + busyString;
}
// Other helper methods
}
Como uma fonte auxiliar pode conter várias implementações auxiliares, o registro é diferente do registro único auxiliar:
@Test
public void whenHelperSourceIsCreated_ThenCanRegister() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
handlebars.registerHelpers(new HelperSource());
// Implementation details
}
Estamos registrando nossos ajudantes usando o métodoHandlebars.registerHelpers(). Além disso,the name of the helper method becomes the name of the helper tag.
7. Reutilização de modelos
A biblioteca do guidão fornece várias maneiras de reutilizar nossos modelos existentes.
7.1. Inclusão de modelo
Template inclusion is one of the approaches for reusing templates. It favors the composition of the templates.
Hi {{name}}!
Este é o conteúdo do modeloheader -header.html.
Para usá-lo em outro modelo, devemos nos referir ao modeloheader.
{{>header}}
This is the page {{name}}
Temos o templatepage– page.html – que inclui o templateheader usando\{\{>header}}.
Quando Handlebars.java processa o modelo, a saída final também conterá o conteúdo deheader:
@Test
public void whenOtherTemplateIsReferenced_ThenCanReuse() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
Template template = handlebars.compile("page");
Person person = new Person();
person.setName("example");
String templateString = template.apply(person);
assertThat(templateString).isEqualTo("Hi example!
\nThis is the page example
");
}
7.2. Herança de modelo
Alternativamente à composição,Handlebars provides the template inheritance.
Podemos alcançar relacionamentos de herança usando as tags\{\{#block}}e\{\{#partial}}:
{{#block "intro"}}
This is the intro
{{/block}}
{{#block "message"}}
{{/block}}
Fazendo isso, o modelomessagebase tem dois blocos -introemessage.
Para aplicar a herança, precisamos substituir essesblocks em outros modelos usando\{\{#partial}}:
{{#partial "message" }}
Hi there!
{{/partial}}
{{> messagebase}}
Este é o modelosimplemessage. Observe que estamos incluindo o modelomessagebase e também substituindo o blocomessage.
8. Sumário
Neste tutorial, vimos Handlebars.java para criar e gerenciar modelos.
Começamos com o uso básico de tags e, em seguida, analisamos as diferentes opções para carregar os modelos de guidão.
Também investigamos os auxiliares de modelos que fornecem uma grande quantidade de funcionalidades. Por fim, analisamos as diferentes maneiras de reutilizar nossos modelos.
Por fim, verifique o código-fonte de todos os exemplosover on GitHub.