Total Pageviews

2020/03/04

[Java] [Freemarker] Using macro to reuse common template fragments

When you find yourself copy-pasting common parts between templates a lot, you should probably use macros.

Assume I create a utils.ftl in /resources/ftl/common as bellows:
<#macro myPage>
    <html>
    <head>
        <title>${title}</title>
    </head>
    <body>
    <h1>${title}</h1>

    <#-- This processes the enclosed content:  -->
    <#nested>

    </body>
    </html>
</#macro>

<#macro otherExample p1 p2>
    <p>The parameters were: ${p1}, ${p2}</p>
</#macro>

helloWorld.ftl will looks like:
<#-- import common template fragments -->
<#import "common/utils.ftl" as u>

<@u.myPage>
    <p>${example.name} by ${example.developer}</p>
    <#-- Just another example of using a macro: -->
    <@u.otherExample p1="Albert" p2="Mandy" />

    <#if systems??>
    <ul>
        <#list systems as system>
            <li>${system_index + 1}. ${system.name} from ${system.developer}</li>
        </#list>
    </ul>
    <#else>
    No data found!
    </#if>

</@u.myPage>

Test case:
package com.esb.batch;

import com.esb.batch.exception.FtlException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class FtlTest {

    private Configuration cfg;

    @Before
    public void init() {
        cfg = new Configuration();
        // 設定到 classpath 讀取 ftl file
        cfg.setClassForTemplateLoading(this.getClass(), "/");
    }

    @Test
    public void testHelloWorld() {
        try (Writer file = new FileWriter(new File("C:/ftl_helloWorld.html"));) {
            Template template = cfg.getTemplate("ftl/helloWorld.ftl");

            Map<String, Object> data = new HashMap<>();
            data.put("title", "Freemarker Example");
            data.put("example", ValueExampleObject.builder().name("Java Object").developer("Albert").build());

            List<ValueExampleObject> systems = new ArrayList<>();
            systems.add(ValueExampleObject.builder().name("Android").developer("Google").build());
            systems.add(ValueExampleObject.builder().name("iOS").developer("Apple").build());
            systems.add(ValueExampleObject.builder().name("Ubuntu").developer("Canonical").build());
            systems.add(ValueExampleObject.builder().name("Windows").developer("Microsoft").build());
            data.put("systems", systems);

            template.process(data, file);
        } catch (IOException | TemplateException e) {
            throw new FtlException("fail to generate file from ftl file : " + e.getMessage(), e);
        }
    }

    @Getter
    @Builder
    public static class ValueExampleObject {
        private String name;
        private String developer;
    }

}

Check the result:
<html>

<head>
    <title>Freemarker Example</title>
</head>

<body>
    <h1>Freemarker Example</h1>

    <p>Java Object by Albert</p>
    <p>The parameters were: Albert, Mandy</p>

    <ul>
        <li>1. Android from Google</li>
        <li>2. iOS from Apple</li>
        <li>3. Ubuntu from Canonical</li>
        <li>4. Windows from Microsoft</li>
    </ul>

</body>

</html>

No comments: