Thymeleaf 自定义标签的打造

由于本人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;
        }*/
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值