Thymeleaf + Spring: 去除默认元素 id

Thymeleaf + Spring: get rid of the default element id

在 Thymeleaf (2.1.4.RELEASE) 中使用 th:field 时,是否有任何方法可以抑制元素的自动生成 ID 属性?例如,给定代码:

<input type="text" th:field="*{year}" />

将产生以下 HTML:

<input type="text" id="year" name="year" value="" />

我要实现的是(没有id属性):

<input type="text" name="year" value="" />

在 JSP 中就像设置空 id 一样简单:

<form:input path="year" id="" />

但是 Thymeleaf 只是用默认生成的属性替换了这个空属性。

如果您不想在输入字段的 ID 中使用它,只需将值分配给 th:name 字段,

<input type="text" th:name="*{year}" />

会给你这样的输出,

<input type="text" name="2015" />

或者你可以在末尾使用一个字符串来让生成的id和name属性不同,像这样

<input type="text" th:name="*{year}" th:id="*{year} + '-year' " />

会给你输出,

<input type="text" name="2015" id="2015-year"/>

好的,我查看了 Thymeleaf 的源代码(2.1.4.RELEASE),在 Spring 方言中负责设置元素 id 的方法是 org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor.doProcess(...) (source on Github) that calls org.thymeleaf.spring4.processor.attr.AbstractSpringFieldAttrProcessor.computeId(...) (source on Github).如果你看computeId(...),你会发现没有简单的方法来设置空id。

所以我们需要用一种不简单的方式来做:) 这里是:
我创建了一个自定义方言并定义了一个自定义属性 noid。标记如下所示:

<input type="text" th:field="*{year}" thex:noid="true" />

有一个很好的 tutorial 解释了如何在 Thymeleaf 中创建和使用自定义方言,下面是最重要的部分:属性处理器负责从给定元素中删除 id 属性。

重要注意事项:

  • 高优先级值(9999)保证本处理器作为最后一个执行(所以本处理器执行后不会有其他处理器修改id)
  • 修改类型设置为替换,因此我们将完全替换 id 元素的值
  • removeAttributeIfEmpty(...) returns 是的,不言自明,如果为空则删除属性
  • getModifiedAttributeValues(...) 将 id 设置为空值,因为上述方法 returns true,id 属性被删除

代码:

    public class NoIdAttrProcessor extends AbstractAttributeModifierAttrProcessor {

        public NoIdAttrProcessor() {
            super("noid");
        }

        @Override
        public int getPrecedence() {
            return 9999;
        }

        @Override
        protected ModificationType getModificationType(Arguments arguments, Element element, String attributeName, String newAttributeName) {
            return ModificationType.SUBSTITUTION;
        }

        @Override
        protected boolean removeAttributeIfEmpty(Arguments arguments, Element element, String attributeName, String newAttributeName) {
            return true;
        }

        @Override
        protected boolean recomputeProcessorsAfterExecution(Arguments arguments, Element element, String attributeName) {
            return false;
        }

        @Override
        protected Map<String, String> getModifiedAttributeValues(Arguments arguments, Element element, String attributeName) {
            Map<String, String> values = new HashMap<>(1);
            values.put("id", "");
            return values;
        }

    }