由于本人E文实在有限,完全看不懂官方文档,这里百度好多文章,统一感谢下那些无私奉献的大大们!
版权声明:严禁用于任何商业用途的转发!
1. 我的需求:
问题在于我有很多类似的页面元素,每次都要写好多前端代码,为了简化代码,因此决定使用Thymeleaf自定义标签。
2.使用自定义标签后的前端代码:
2.1 *{parent.id}、*{parent.treeName}等为Thymeleaf标准表达式,如果要动态获取属性值,需要在自定义解析器中解析。
ThymeleafProcessor类中的getExpressionValue方法用于解析Thymeleaf标准表达式,这里我统一返回String类型,请勿直接照搬,需根据项目需求改写!
<thl:tml type="treeSelect" excludeCode="companyId" isShowCode="1" title = "上级组织信息"
url = "/a/company/treeData" name = "parent.id" id = "parent"
submitValue = "*{parent.id}" showValue = "*{parent.treeName}"
canSelectRoot = "true" canSelectParent = "true"/>
3. 自定义标签构建代码:
3.1 Thymeleaf 自定义注解
package org.bluedream.dream.utils.thymeleaf;
import org.springframework.stereotype.Component;
import org.thymeleaf.dialect.AbstractProcessorDialect;
import org.thymeleaf.dialect.IProcessorDialect;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.standard.StandardDialect;
import java.util.HashSet;
import java.util.Set;
/**
* Thymeleaf 自定义注解
*/
@Component
public class ThymeleafEntrance extends AbstractProcessorDialect implements IProcessorDialect {
// 定义方言名称
private static final String DIALECT_NAME = "thl-dialect";
// 要应用于名称的匹配前缀
private static final String PREFIX = "thl";
public ThymeleafEntrance() {
super(DIALECT_NAME, PREFIX, StandardDialect.PROCESSOR_PRECEDENCE);
}
/**
* 元素处理器
* @param s
* @return
*/
@Override
public Set<IProcessor> getProcessors(String s) {
Set<IProcessor> processors = new HashSet<IProcessor>();
processors.add(new ThymeleafProcessor(PREFIX));
return processors;
}
}
3.2 自定义标签逻辑类:EmptyUtil判空工具类为私有工具类,判空时请自行修改代码。
package org.bluedream.dream.utils.thymeleaf;
import com.alibaba.fastjson.JSONObject;
import org.bluedream.dream.utils.EmptyUtil;
import org.springframework.context.ApplicationContext;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractElementTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.spring5.context.SpringContextUtils;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.thymeleaf.templatemode.TemplateMode;
/**
* 自定义标签逻辑类
*/
public class ThymeleafProcessor extends AbstractElementTagProcessor {
//定义方言 标签
private static final String TAG_NAME = "tml";
private static final int PRECEDENCE = 10000;
// 自定义属性 type:标签类型解析入口
private static final String TAG_TYPE = "type";
// todo: 自定义标签 类型 测试变量
private static final String TAG_TYPE_DICT = "treeSelect";
private static final String TAG_URL = "url"; //请求地址
private static final String TAG_TITLE = "title"; //标题
private static final String TAG_ACTIVE_NAME = "name"; //表单绑定name
private static final String TAG_ACTIVE_ID = "id"; //表单绑定id
private static final String TAG_SUBMIT = "submitValue"; //提交值
private static final String TAG_SHOW = "showValue"; //回显内容
private static final String TAG_CAN_SELECT_ROOT = "canSelectRoot"; //根节点是否可选
private static final String TAG_CAN_SELECT_PARENT = "canSelectParent"; //父节点是否可选
private static final String TAG_EXCLUDE_CODE = "excludeCode"; //排除节点 元素name的值
private static final String TAG_SHOW_CODE = "isShowCode"; //是否显示编码
private boolean firstLoad = true;
public ThymeleafProcessor(final String dialectPrefix) {
super(
TemplateMode.HTML, // 此处理器将仅应用于HTML模式
dialectPrefix, // 要应用于名称的匹配前缀
TAG_NAME, // 标签名称:匹配此名称的特定标签
true, // 将标签前缀应用于标签名称
null, // 无属性名称:将通过标签名称匹配
false, // 没有要应用于属性名称的前缀
PRECEDENCE // 优先(内部方言自己的优先)
);
}
/**
* 核心处理器
* @param context 上下文对象
* @param tag 当前节点对象
* @param handler
*/
@Override
protected void doProcess(ITemplateContext context, IProcessableElementTag tag, IElementTagStructureHandler handler) {
// 获取应用程序上下文。
init(context);
// 获取标签类型
String tagTypeName = tag.getAttribute(TAG_TYPE).getValue();;
IModel rootModel = null;
// todo: TAG_TYPE_DICT常量用于测试:当treeSelect时,创建遮罩弹出的zTree页面
if (EmptyUtil.isNoEmpty(tagTypeName) && TAG_TYPE_DICT.equals(tagTypeName)){
rootModel = getTreeSelectTag(context, tag);
}
handler.replaceWith(rootModel, false);
}
/**
* 解析Thymeleaf标准表达式
* @param context 上下文
* @param expressionString Thymeleaf标准表达式 Value
* @return
*/
private String getExpressionValue(ITemplateContext context , String expressionString){
// todo:使用IStandardExpression 解析Thymeleaf标准表达式,try...catch不抛异常,catch中 赋予空字符串
// 引擎配置
IEngineConfiguration configuration = context.getConfiguration();
// 获取 Thymeleaf标准表达式解析器
IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);
// 用于解析Thymeleaf标准表达式
IStandardExpression expression = parser.parseExpression(context , expressionString);
try {
return (String) expression.execute(context);
}catch (Exception e){
return "";
}
}
/**
* 返回 zTree IModel
* @param context
* @param tag
* @return
*/
private IModel getTreeSelectTag(ITemplateContext context, IProcessableElementTag tag) {
// 遮罩层 title
String treeTitle = tag.getAttribute(TAG_TITLE).getValue();
// zTree nodes的请求地址
String treeUrl = tag.getAttribute(TAG_URL).getValue();
//回显input 标签 name
String treeActiveName = tag.getAttribute(TAG_ACTIVE_NAME).getValue();
//回显input 标签 id
String treeActiveId = tag.getAttribute(TAG_ACTIVE_ID).getValue();
//zTree 根节点是否允许选择
String treeSelectRoot = tag.getAttribute(TAG_CAN_SELECT_ROOT).getValue();
//zTree 父节点 是否允许选择
String treeSelectParent = tag.getAttribute(TAG_CAN_SELECT_PARENT).getValue();
//排除节点
String excludeCode = tag.getAttribute(TAG_EXCLUDE_CODE).getValue();
//是否显示 编码
String isShowCode = tag.getAttribute(TAG_SHOW_CODE).getValue();
//表单提交时,zTree提交值 名称
String treeSubmit = tag.getAttribute(TAG_SUBMIT).getValue();
treeSubmit = getExpressionValue(context , treeSubmit);
//表单显示时,回显内容 名称
String treeShowValue = tag.getAttribute(TAG_SHOW).getValue();
treeShowValue = getExpressionValue(context , treeShowValue);
IModelFactory factory = context.getModelFactory();
IModel rootModel = factory.createModel(); //页面div根节点
IModel submitModel = factory.createModel(); //form表单中隐藏的父级id input标签
IModel showInputModel = factory.createModel(); //form中回显的父级名称
//zTreeOpen函数的参数
String treeOption = "{" +
"title:'" + treeTitle + "'," +
"url:'" + treeUrl + "'," +
"echoId:'" + treeActiveId + "'," +
"canSelectRoot:" + treeSelectRoot + "," +
"canSelectParent:" + treeSelectParent + "," +
"excludeCode:'" + excludeCode + "'," +
"isShowCode:'" + isShowCode + "'" +
"}";
// 将 treeOption 参数转为JSON
JSONObject optionJSON = JSONObject.parseObject(treeOption);
String classStyle = "input-group"; //定义class属性
// todo:创建闭合标签:createOpenElementTag 创建开始标签,createCloseElementTag 创建结束标签
// todo:非闭合标签:仅createOpenElementTag即可
rootModel.add(factory.createOpenElementTag(String.format("div class='%s'" , classStyle))); //div 开始标签
// form表单中隐藏的父级id input标签
String inputId = treeActiveId + "Code";
submitModel.add(factory.createOpenElementTag(String.format("input type='hidden' id='%s' name='%s' value='%s'" , inputId , treeActiveName , treeSubmit)));
rootModel.addModel(submitModel);
// form中回显的父级名称
classStyle = "form-control";
inputId = treeActiveId + "Name";
showInputModel.add(factory.createOpenElementTag(String.format("input class='%s' type='text' id='%s' value='%s' autocomplete='off' readonly onclick='zTreeOpen(%s)'/" , classStyle , inputId , treeShowValue , optionJSON)));
rootModel.addModel(showInputModel);
// 搜索图标div
final IModel btnDivModel = factory.createModel();
classStyle = "input-group-btn";
btnDivModel.add(factory.createOpenElementTag(String.format("div class='%s'" , classStyle)));
// 搜索图标 内部button
final IModel btnThisModel = factory.createModel();
classStyle = "btn btn-default";
btnThisModel.add(factory.createOpenElementTag(String.format("button type='button' class='%s' onclick='zTreeOpen(%s)'" , classStyle , optionJSON)));
// 搜索图标 btn中的i 标签
final IModel iModel = factory.createModel();
classStyle = "glyphicon glyphicon-search";
iModel.add(factory.createOpenElementTag(String.format("i class='%s'" , classStyle)));
iModel.add(factory.createCloseElementTag("i"));
btnThisModel.addModel(iModel);
btnThisModel.add(factory.createCloseElementTag("button"));
// button 按钮添加至btn的DIV中
btnDivModel.addModel(btnThisModel);
btnDivModel.add(factory.createCloseElementTag("div"));
rootModel.addModel(btnDivModel);
rootModel.add(factory.createCloseElementTag("div")); //div 闭合标签
return rootModel;
}
/**
* 获取应用程序上下文。
* @param context
*/
private void init(ITemplateContext context){
ApplicationContext appCtx = SpringContextUtils.getApplicationContext((ITemplateContext) context);
/* if (firstLoad){
firstLoad = false;
}*/
}
}