How do I properly process HTML tag and its body in my own Element Model Processor? For example, I have the following HTML structure:
<my:form id="some_id">
<my:input type="text" name="input-1"/>
<my:input type="number" name="input-2"/>
</my:form>
I would like to alter both the wrapping my:form
tag by adding some generated attribute so that output would look something like <form id="some_id" other-attr="_generated_value_">...</form>
and process each of the inner my:input
by adding an attribute with form id to each of those inputs (and generally process those tags as they should be), e.g. <input type="text" name="input-1" id="generated_1" form-id="some_id">
. Normally my:input
is processed by my AbstractAttributeTagProcessor
extension.
As of now I have extended the AbstractElementModelProcessor
creating the processor like this:
public class MyFormProcessor extends AbstractElementModelProcessor {
private static final String ELEMENT_NAME = "form";
private static final int PRECEDENCE = 1000;
public MyFormProcessor( final String dialectPrefix ) {
super(
TemplateMode.HTML,
dialectPrefix, // dialect prefix - 'my' in this case
ELEMENT_NAME,
true, // filter by element name prefix
null,
false,
PRECEDENCE
);
}
@Override
protected void doProcess( ITemplateContext context,
IModel model,
IElementModelStructureHandler structureHandler ) {
// what goes here?
}
}
The processor is registered correctly and does turm the form and its body into a series of events, but I'm yet to figure out how to modify the model
properly as I cannot find any modification methods on its elements.
Thanks in advance for any guidance!
To make your example work, I needed to make a change to your custom Thymeleaf input structure:
<my:form id="some_id">
<my:input type="text" name="input-1">
<my:input type="number" name="input-2">
</my:form>
The difference is that I do not self-close the <my:input>
tags (they end with >
not with />
. This is so that they reflect the structure of the target "standalone" <input>
elements, which are also not self-closing.
My approach processes the entire fragment of input HTML - so, if you also already have a separate processor for <my:input>
tags, then I assume this would need to be a higher priority than that.
Here is the doProcess()
method, with comments in the code:
@Override
protected void doProcess(ITemplateContext context,
IModel model,
IElementModelStructureHandler structureHandler) {
final IModelFactory modelFactory = context.getModelFactory();
// first handle the form element:
String formID = null;
final ITemplateEvent formEvent = model.get(0);
if (formEvent instanceof IOpenElementTag) {
// retrieve the form's ID value:
IOpenElementTag ele = (IOpenElementTag) formEvent;
IAttribute attr = ele.getAttribute("id");
formID = attr.getValue();
}
// build the attributes we want the form element to use:
Map<String, String> formAttrs = new HashMap<>();
formAttrs.put("id", formID);
formAttrs.put("other-attr", "_generated_value_");
// create the form element:
IOpenElementTag formOpen = modelFactory
.createOpenElementTag("form", formAttrs,
AttributeValueQuotes.DOUBLE,
false);
model.replace(0, formOpen);
int idInt = 1; // used to increment ID values
// the loop processes all of the elements inside the form's opening
// and closing tags:
for (int i = 1; i < model.size() - 1; i++) {
final ITemplateEvent inputEvent = model.get(i);
if (inputEvent instanceof IOpenElementTag) {
IOpenElementTag ele = (IOpenElementTag) inputEvent;
// we will add some more attributes here:
Map<String, String> attrs = ele.getAttributeMap();
attrs.put("form-id", formID);
attrs.put("id", "generated_" + idInt++); // the "idInt++" is just a demo - use whatever sequence you want here
IOpenElementTag openEle = modelFactory
.createOpenElementTag("input", attrs,
AttributeValueQuotes.DOUBLE,
false);
model.replace(i, openEle);
}
}
}
The end-result HTML is as follows:
<form other-attr="_generated_value_" id="some_id">
<input type="text" name="input-1" form-id="some_id" id="generated_1">
<input type="number" name="input-2" form-id="some_id" id="generated_2">
</form>
Notes
There may be more you could do to re-factor the code.
There are probably alternative (and maybe better) approaches to the one shown here. With Thymeleaf, there is often more than one way to do something.