Thymeleaf custom dialect with standard expression

Viewed 1302

I have created a custom Thymeleaf 3 dialect and it works fine if I use it only with static attribute values like this:

<formBuilder:inputRow title="Email:" name="email" value="test@test.com" />

However I want to use standard Thymeleaf expressions to fill the value attribute but if I write this:

<formBuilder:inputRow title="Email:" name="email" value="${user.email}" />

The output will be the text ${user.email} instead of the real email address.

If I use th:value like this:

<formBuilder:inputRow title="Email:" name="email" th:value="${user.email}" />

then no value field is generated at all.

The dialect class is the following. It registers only an elementTag processor:

public class FormBuilderDialect extends AbstractProcessorDialect {

    private static final String DIALECT_NAME = "Form Builder Dialect";

    public FormBuilderDialect() {
        super(DIALECT_NAME, "formBuilder", StandardDialect.PROCESSOR_PRECEDENCE);
    }

    @Override
    public Set<IProcessor> getProcessors(String dialectPrefix) {
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add(new InputElementTagProcessor(dialectPrefix));
        return processors;
    }
}

And the element processor is:

public class InputElementTagProcessor extends AbstractElementTagProcessor {

    private static final String TAG_NAME = "inputRow";
    private static final int PRECEDENCE = 1000;

    public InputElementTagProcessor(final String dialectPrefix) {
        super(
                TemplateMode.HTML, // This processor will apply only to HTML mode
                dialectPrefix,     // Prefix to be applied to name for matching
                TAG_NAME,          // Tag name: match specifically this tag
                true,              // Apply dialect prefix to tag name
                null,              // No attribute name: will match by tag name
                false,             // No prefix to be applied to attribute name
                PRECEDENCE);       // Precedence (inside dialect's own precedence)
    }

    @Override
    protected void doProcess(ITemplateContext context, IProcessableElementTag tag, IElementTagStructureHandler structureHandler) {

        final String name = tag.getAttributeValue("name");
        final String value = tag.getAttributeValue("value");

        final IModelFactory modelFactory = context.getModelFactory();

        final IModel model = modelFactory.createModel();

        // Add input
        Map<String, String> inputAttributes = new HashMap<>();
        inputAttributes.put("name", name);
        inputAttributes.put("value", value);
        model.add(modelFactory.createStandaloneElementTag("input", inputAttributes, AttributeValueQuotes.DOUBLE, false, false));

        structureHandler.replaceWith(model, false);
    }
}

Any idea how to solve this?

2 Answers

To solve the problem I had to create an attribute tag processor like this:

public class FormBuilderAttributeTagProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTR_NAME = "value";
    private static final int PRECEDENCE = 100;

    public FormBuilderAttributeTagProcessor(final String dialectPrefix) {
        super(
                TemplateMode.HTML,
                dialectPrefix,
                "inputRow",
                true,
                ATTR_NAME,
                false,
                PRECEDENCE,
                false);
    }

    @Override
    protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, String attributeValue, IElementTagStructureHandler structureHandler) {
        final IEngineConfiguration configuration = context.getConfiguration();

        final IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);

        final IStandardExpression expression = parser.parseExpression(context, attributeValue);

        final String result = (String) expression.execute(context);

        structureHandler.setAttribute("value", result);
    }
}

And register it as processor in the dialect class:

public class FormBuilderDialect extends AbstractProcessorDialect {

    private static final String DIALECT_NAME = "Form Builder Dialect";

    public FormBuilderDialect() {
        super(DIALECT_NAME, "formBuilder", StandardDialect.PROCESSOR_PRECEDENCE);
    }

    @Override
    public Set<IProcessor> getProcessors(String dialectPrefix) {
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add(new InputElementTagProcessor(dialectPrefix));
        processors.add(new FormBuilderAttributeTagProcessor(dialectPrefix));
        return processors;
    }
}
Related