Thymeleaf 3 Modifying global model attributes in custom processor

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Thymeleaf 3 Modifying global model attributes in custom processor

jaisenbrey
We're currently upgrading from Thymeleaf 2 to Thymeleaf 3 and we've ran into a problem with trying to add attributes to the global model. We were doing this in Thymeleaf 2 like this:

public abstract class AbstractModelVariableModifierProcessor extends AbstractElementProcessor {
...
    protected void addToModel(Arguments arguments, String key, Object value) {
        ((Map<String, Object>) arguments.getExpressionEvaluationRoot()).put(key, value);
    }
...
}

Since "Arguments" was removed in Thymeleaf 3 the method signature is now something more along the lines of:

public abstract class AbstractModelVariableModifierProcessor extends AbstractElementTagProcessor {
...
    protected void addToModel(ITemplateContext context,
                                            IElementTagStructureHandler structureHandler,
                                            String key, Object value) {

    }
...
}

After some research we tried to casting "context" to an "IEngineContext" and used "setVariable" but we found that this was just local. We looked into why that was and we were brought to "ThymeleafView.renderFragment" where the Spring model and other Spring request information was set on a "WebExpressionContext" object which is used to create the "EngineContext" that's sent to processors. From what we can see all the information on the "WebExpressionContext" is copied to an "EngineContext" for each processor therefore any changes made to the variables on the context sent to the processor isn't persisted to any global state.

That brings us to the original question which is how can we change global model attributes, like we were doing in TL2, in TL3? Or is this even possible anymore in TL3?
Reply | Threaded
Open this post in threaded view
|

Re: Thymeleaf 3 Modifying global model attributes in custom processor

danielfernandez
Administrator
I see. What you were doing was to directly access the VariablesMap instance being used underneath by the Arguments object, and directly modify it in order to avoid the standard thymeleaf behaviour of local variables being precisely that, local (to the part of the code hierarchy they are defined in).

The problem is, now in Thymeleaf 3.0 it is the IContext implementation itself (actually the IEngineContext subinterface implementation) the one that keeps the required accounting for local variables, i.e. the levels of markup at which each variable has been created, and therefore triggers their removal when the corresponding markup blocks are closed.

So variables put into the context inside an <x> element will only live inside the <x>.

How to still do global variables? I see two easy possibilities:

1. Use the HttpServletRequest directly. This binds you to the Servlet API, but in case you are already bound to it, might be the easiest way. Attributes set into the HttpServletRequest are ALWAYS available at any level in the hierarchy of markup, for better integration with other non-thymeleaf template technologies. DISCLAIMER: I don't personally like this option :)

2. Create your own variable holder object (a Map<String,Object> will do, but you can create a specialized bean if you want) and make it MUTABLE so that it can be modified by your processors. Then simply either add it to the Model at your controllers or (probably more elegant) make your dialect provide an implementation of ITemplateBoundariesProcessor, which can do things at template processing start such as adding an object of your variable holder class (or Map) to the context as a local variable but at "level zero" of the hierarchy (we haven't started processing yet!), so that it is available everywhere.
Reply | Threaded
Open this post in threaded view
|

Re: Thymeleaf 3 Modifying global model attributes in custom processor

jaisenbrey
Thank you for the quick response! I went ahead and implemented your first suggestion since we already modify the HttpServletRequest in a similar way in the framework for other things and it worked perfectly. Since some of our processors not only add variables to the global state but also modify those variables (sometimes multiple times) further in the template we weren't sure if the second option would allow that to happen. We do have concerns that when/if Thymeleaf 3 becomes multithreaded that our solution may not work for when the global variable gets changed multiple times, but we are going to advise against modifying global state with processors along with, internally, changing from changing global state to just using tag scoped local variables.

Thank you again for the help and congrats on rolling out TL3!