自定义模板标签、函数

作者:  最后修改:2016年10月10日  浏览数:139

模板中可以通过模板标签和el表达式及函数用来获取动态数据,控制模板执行流程 
在此文中我们介绍如何自定义模板标签和表达式函数

1.自定义标签

标签具有标签名、标签属性、标签体 
标签可以控制呈现内容,执行流程

实现步骤

  • 实现扩展项(com.zving.framework.ui.zhtml.ZhtmlTagService)

  • 实现抽象方法

  • 定义标签属性,实现get\set方法

  • 实现流程方法

流程控制方法: 
I. doStartTag: 标签开始执行的时候调用, 
返回值:

EVAL_BODY_INCLUDE-表示执行标签体(默认
EVAL_BODY_BUFFERED-执行标签体,并将执行结果放在buffer缓存中 
SKIP_BODY-跳过标签体 
SKIP_PAGE-跳过页面

II.doAfterBody:标签体执行完成时调用, 
返回值

EVAL_PAGE 执行标签后面的页面内容(默认
EVAL_BODY_AGAIN 重新执行标签体

III.doEndTag:整个标签执行完成时调用 
一般在此处获取buffer中的内容,对buffer进行处理 
返回值

SKIP_PAGE 
EVAL_PAGE(默认)

AbstractTag常用属性和方法

context:模板执行上下文 
常用方法:

addDataVariable :添加一个变量数据,可以在标签体范围内使用el表达式和变量名获取到变量值 
addRootVariable: 向模板执行上下文中添加一个变量,可以在页面范围内使用el表达式和变量名获取到变量值 
getOut:返回当前的TemplateWriter实例,可以用该对象输出内容或获取缓存内容 
eval: 执行一段表达式(一般为el表达式或变量名)

示例:

在该示例中实现了一个执行自定义sql并使用标签体循环呈现查询结果的标签

创建扩展项

Selection_186

图1. 创建扩展项SQLInvokeTag



package com.zving.demo.tag;

import java.util.ArrayList;
import java.util.List;

import com.zving.demo.DemoPlugin;
import com.zving.framework.data.DataTable;
import com.zving.framework.data.DataTypes;
import com.zving.framework.data.QueryBuilder;
import com.zving.framework.template.AbstractTag;
import com.zving.framework.template.TagAttr;
import com.zving.framework.template.exception.TemplateRuntimeException;
import com.zving.framework.utility.StringUtil;

public class SQLInvokeTag extends AbstractTag {

protected String sql;
protected int pageIndex;
protected int pageSize;
protected DataTable dt;
protected int index;

public SQLInvokeTag() {
super();
System.out.println("SQLInvokeTag 构造执行!!!!");
}

@Override
public String getDescription() {
return "执行SQL语句,循环输出结果到标签体";
}

@Override
public String getExtendItemName() {
return "SQLInvokeTag";
}

@Override
public String getPluginID() {
return DemoPlugin.ID;
}

@Override
public String getPrefix() {
return "z";
}

@Override
public String getTagName() {
return "sqlInvoke";
}

@Override
public List<TagAttr> getTagAttrs() {
/* 定义标签属性 */
List<TagAttr> list = new ArrayList<TagAttr>();
list.add(new TagAttr("sql", false, DataTypes.STRING, "要执行的SQL语句"));
list.add(new TagAttr("pageIndex", false, DataTypes.INTEGER, "当前页码"));
list.add(new TagAttr("pageSize", false, DataTypes.INTEGER, "分页大小"));
return list;
}

@Override
public int doStartTag() throws TemplateRuntimeException {
System.out.println("Do start Tag!~!!");
QueryBuilder qb = new QueryBuilder(sql);
/* 如果指定了分页大小执行分页查询,否则查询全部 */
if (StringUtil.isNotEmpty(getAttribute("pageSize"))) {
dt = qb.executePagedDataTable(pageSize, pageIndex);
} else {
dt = qb.executeDataTable();
}
/* 如果查询到结果,取出第一行数据放到上下文变量Value中,执行标签体,否则跳过标签体 */
if (dt.getRowCount() > 0) {
index = 0;// 数据索引从0开始
context.addDataVariable("Value", dt.getDataRow(0));// 当索引数据
context.addDataVariable("Index", index);// 当前数据索引
return EVAL_BODY_INCLUDE;
} else {
return SKIP_BODY;
}
}

@Override
public int doAfterBody() throws TemplateRuntimeException {
System.out.println("Do doAfterBody!~!!");
index = index + 1;
/* 标签体执行完成,数据索引+1,如果索引超出了查询结果数,执行标签后面的页面,否则取出当前索引数据,执行重新执行标签体 */
if (index >= dt.getRowCount()) {
return EVAL_PAGE;// 执行标签后面的页面内容
} else {
context.addDataVariable("Value", dt.getDataRow(index));// 当前索引数据
context.addDataVariable("Index", index);// 当前索引
return EVAL_BODY_AGAIN;
}
}

@Override
public int doEndTag() throws TemplateRuntimeException {
return super.doEndTag();
}

public String getSql() {
return sql;
}

public void setSql(String sql) {
this.sql = sql;
}

public int getPageIndex() {
return pageIndex;
}

public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}

public int getPageSize() {
return pageSize;
}

public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}

}

代码1. 实现SQLInvokeTag类,实现标签逻辑


<hr>
sqlInvoke start:
<ul>
<z:sqlInvoke sql="select * from ZDUser" pageIndex="0" pageSize="5">
<li> ${Index }:${Value.UserName} </li>
</z:sqlInvoke>
</ul>
sqlInvoke end!

代码2. 标签使用测试,查询ZDUser 前5行数据


Selection_187

图2. 页面执行呈现结果

页面执行呈现结果 


2.自定义页面函数

函数具有方法名、参数列表、返回值

实现步骤

  • 实现扩展项(扩展服务ID:com.zving.framework.ui.zhtml.ZhtmlFunctionService)

  • 实现接口(com.zving.framework.expression.IFunction)或者 继承(com.zving.framework.expression.AbstractFunction)

  • 定义参数类型

  • 实现函数方法得到返回值

示例:

该示例中实现了反射调用传入对象指定方法的表达式函数

添加扩展项Invoke

Selection_188

图3.添加扩展项



package com.zving.dynamic.function;

import java.lang.reflect.Method;

import com.zving.framework.expression.AbstractFunction;
import com.zving.framework.expression.ExpressionException;
import com.zving.framework.expression.IVariableResolver;

public class Invoke extends AbstractFunction {

@Override
public String getFunctionPrefix() {
return "";
}

@Override
public String getFunctionName() {
return "invoke";
}

@Override
public Class<?>[] getArgumentTypes() {
/*定义参数类型*/
return new Class<?>[] { Object.class, Object.class };
}

@Override
public Object execute(IVariableResolver resolver, Object... args) throws ExpressionException {
/*判断是否传入参数*/
if (args == null || args.length == 0) {
return null;
}
if (args.length == 1) {
return null;
} else if (args.length >= 2) {
/*当传入参数数量大于等于2时执行*/
Object o = args[0];//第一个参数为执行方法的实例对象
String method = (String) args[1];//第二个参数为要执行的方法

/*剩余参数为要执行的方法参数*/
Class<?>[] paramTypes = new Class<?>[args.length - 2];
Object[] params = new Object[args.length - 2];
for (int i = 2; i < args.length; i++) {
paramTypes[i - 2] = args[i].getClass();
params[i - 2] = args[i];
}

/*获取到要执行的方法,并执行*/
Class<?> clazz = o.getClass();
try {
Method m = clazz.getMethod(method, paramTypes);
return m.invoke(o, params);//返回执行结果
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}

}

代码3. 实现Invoke类


客户端IP:${invoke(Request,"getClientIP")}
<hr>
客户端:${invoke(Request,"getHeaders")['user-agent']}

代码4. 测试页面代码


Selection_190

图4. 访问测试页面执行结果