freemarker 自定义布局指令
在项目中使用freemarker模板引擎时,经常感觉布局上有些功能没法实现,如下:
<html>
<head>
<title>xxxx</title>
<#inclue “url1”>
</head>
<body>
<div><#include “header.ftl”></div>
<div>
<div><#inclued “left.ftl”></div>
<div>
part1
</div>
<div>
part2
</div>
</div>
<div>
part3
</div>
<div><#include “footer.ftl”></div>
</body>
</html>
其实大部分页面都是这种模板,只是改动某一块或者几块,但是每个ftl文件,都得包含这些内容,万一有改动,得全部ftl做修改,比较麻烦。
假设自定义三个指令parent, layout, content ,可能会方便很多。
layout.ftl
<html>
<head>
<@layout name=“title">
<title>default title</title>
</@layout>
<#inclue “url1”>
</head>
<body>
<div><#include “header.ftl”></div>
<div>
<div><#inclued “left.ftl”></div>
<div>
<@layout name=“part1">
default part1
</@layout>
</div>
<div>
<@layout name=“part2">
default part2
</@layout>
</div>
</div>
<div>
<@layout name=“part3">
default part3
</@layout>
</div>
<div><#include “footer.ftl”></div>
</body>
</html>
part1.ftl
<@content name=“title”>
<title>part1</title>
<@content>
<@content name=“part1”>
part1 …..
</@content>
<@parent name=“layout.ftl”/>
part2.ftl
<@content name=“title”>
<title>part2</title>
<@content>
<@content name=“part1”>
part2 …..
</@content>
<@parent name=“layout.ftl”/>
part3.ftl
<@content name=“title”>
<title>part3</title>
<@content>
<@content name=“part1”>
part3 …..
</@content>
<@parent name=“layout.ftl”/>
part1.ftl解析完全部的文本格式为:
<html>
<head>
<title>part1</title>
<#inclue “url1”>
</head>
<body>
<div><#include “header.ftl”></div>
<div>
<div><#inclued “left.ftl”></div>
<div>
part1 …..
</div>
<div>
default part2
</div>
</div>
<div>
default part3
</div>
<div><#include “footer.ftl”></div>
</body>
</html>
part2.ftl,part3.ftl 可脑补出来了。
这三个标签怎么定义呢?
public class ContentDirective implements TemplateDirectiveModel { public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { // 工具方法,获取指令名 String name = ParamUtils.getDirectiveName(params) ; // body类的代理类,主要是由于env存放时现在对象为TemplateModel的子类或者本身 TemplateDirectiveBodyOverrideWraper current = new TemplateDirectiveBodyOverrideWraper(body); // 把content的内容,先保存着 env.setVariable(name, current); } } public class LayoutDirective implements TemplateDirectiveModel { public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { // 工具方法,获取指令名 String name = ParamUtils.getDirectiveName(params) ; // 找寻之前是否有保存 TemplateDirectiveBodyOverrideWraper overrideBody = (TemplateDirectiveBodyOverrideWraper) env.getVariable(name) ; if(overrideBody == null) { // 没有保存,是否有默认 if(body != null) { // 输出默认的 body.render(env.getOut()); } }else { // 输出保存的 overrideBody.render(env.getOut()); } } } public class ParentDirective implements TemplateDirectiveModel { public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { String name = ParamUtils.getParamValue(params,"name"); String encoding = ParamUtils.getParamValue(params, "encoding",null); // 引入文件 env.include(name, encoding, true); } }
当然,freemarker模板引擎解析ftl文件时,是从上往下循着标签解析,在回到之前的样例part1.ftl结构,大概就明白了。
为方便理解,在补上其他两个类的源码:
public class ParamUtils { private static String PREFIX = "_freemarker_directive_prefix_"; public static String getDirectiveName(@SuppressWarnings("rawtypes") Map params) throws TemplateModelException { return PREFIX + getParamValue(params, "name") ; } public static String getParamValue(@SuppressWarnings("rawtypes") Map params, String key) throws TemplateModelException { Object value = params.get(key); if(value == null || "".equals(value.toString())) { throw new TemplateModelException("not found directive property : name"); } return value.toString() ; } public static String getParamValue(@SuppressWarnings("rawtypes") Map params, String key, String defaultValue) throws TemplateException { Object value = params.get(key); return value == null ? defaultValue : value.toString(); } } public class TemplateDirectiveBodyOverrideWraper implements TemplateDirectiveBody,TemplateModel { private TemplateDirectiveBody body; public TemplateDirectiveBodyOverrideWraper(TemplateDirectiveBody body) { super(); this.body = body; } public void render(Writer out) throws TemplateException, IOException { if(body == null) return; body.render(out); } }
也可以上github上查看:https://github.com/jxlzl1988/FreemarkerDirective
松下问童子,言师采药去。
只言此山中,云深不知处。

浙公网安备 33010602011771号