有同学需要 freemarker 的 nested 用法,现给出代码实现
1、添加 #slot 指令
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
import com.jfinal.template.stat.ast.Stat;
public class SlotDirective extends Directive {
public void exec(Env env, Scope scope, Writer writer) {
Stat stat = (Stat)scope.get(InsertDirective._SLOT_TEMPLATE_KEY_);
stat.exec(env, scope, writer);
}
}2、添加 #insert 指令
import java.util.ArrayList;
import java.util.List;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
import com.jfinal.template.stat.ast.Define;
public class InsertDirective extends Directive {
static final String _SLOT_TEMPLATE_KEY_ = "_SLOT_TEMPLATE_KEY_";
private String slotTemplate;
private ExprList exprList;
public void setExprList(ExprList exprList) {
if (exprList.length() < 1) {
throw new ParseException("function name of slot template cant not be null", location);
}
if (! (exprList.getExpr(0) instanceof Const) ) {
throw new ParseException("function name of slot template must be String", location);
}
Const c = (Const)exprList.getExpr(0);
if (! c.isStr()) {
throw new ParseException("function name of slot template must be String", location);
}
this.slotTemplate = c.getStr();
this.exprList = getExprList(exprList);
}
/**
* 第一个参数以后的参数作为 slot 所属模板定义之处的参数
*/
private ExprList getExprList(ExprList exprList) {
if (exprList.length() == 1) {
return ExprList.NULL_EXPR_LIST;
}
List<Expr> ret = new ArrayList<>();
for (int i=1; i<exprList.length(); i++) {
ret.add(exprList.getExpr(i));
}
return new ExprList(ret);
}
public void exec(Env env, Scope scope, Writer writer) {
Define function = env.getFunction(slotTemplate);
if (function == null) {
throw new TemplateException("Template function not defined: " + slotTemplate, location);
}
scope.set(_SLOT_TEMPLATE_KEY_, this.stat);
function.call(env, scope, this.exprList, writer);
}
public boolean hasEnd() {
return true;
}
}3、测试用的模板
在 src/main/resources 目录下创建一个名为 slot.jf 的模板文件(文件名与 java 测试代码保持一致即可)内容如下:
#define template(list, value)
#for(x : list)
#slot()
#end
#(value)
#end
### 第一个参数为函数名,后续所有参数为传递给 slot 所在模板的函数的参数
#insert("template", [1..12], "再传一个参数 value")
<div>这里是向 slot 插入的内容 = #(x) </div>
#end4、测试用的 java 代码
import com.jfinal.template.Engine;
public class Test {
public static void main(String[] args) {
Engine engine = Engine.use().setToClassPathSourceFactory();
engine.addDirective("insert", InsertDirective.class);
engine.addDirective("slot", SlotDirective.class);
// render 方法中可以传入参数供模板中使用
String ret = engine.getTemplate("slot.jf").renderToString(null);
System.out.println(ret);
}
}要点:freemarker 的 nested 在 enjoy 之下可以看成是模板函数与 slot 调用次序的倒置,所以实现起来易如反掌
Enjoy 引擎虽然概念极少,学习成本极低,但功能却十分强大,碰到个别没有的功能也可通过 enjoy 提供的各种扩展机制进行扩展。jfinal 自带的 sql 模板功能也是这样扩展而来的