Processing loaded fragment

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

Processing loaded fragment

Sorceror
Hi guys,
I have little bit specific question for loading fragments.

We're are dynamically load fragments in our forms framework, which generate forms with Java reflection just from form bean definition. Thing is that loaded fragment is processed (don't know why), after closing form tag is processed. Because we carry form context only from opening to closing tag of selected form (because more forms could be on same page), exception is occurring, while processing of loaded fragments is postponed by thymeleaf.

Code for loading fragments:
public static List<Node> loadFragment(Arguments arguments, String fragmentName, String fragmentPath) {
        IFragmentSpec fragmentSpec = new ElementAndAttributeNameFragmentSpec(null, "th:fragment", fragmentName, true);
        FragmentAndTarget fat = new FragmentAndTarget(fragmentPath, fragmentSpec);
        return fat.extractFragment(arguments.getConfiguration(), arguments, arguments.getTemplateRepository());
}

This code is called in InitializerElProcessor, where whole form is builded.
Fragment is loaded as it should be, but it's not processed by thymeleaf. How to load fragment already processed by thymeleaf? Or how should we force thymeleaf to process this nodes (result of loading) immediately?

Thanks for any ideas.
Paul
Reply | Threaded
Open this post in threaded view
|

Re: Processing loaded fragment

Sorceror
Reply | Threaded
Open this post in threaded view
|

Re: Processing loaded fragment

danielfernandez
Administrator
Hi,

I'm not sure I understand your question, but I'll try to explain how fragment inclusion works in case it allows you to find your answers.

When a fragment is included in a template with "th:include" or "th:substituteby", what Thymeleaf does is:

   1. Read and parse (if not already cached) the template the fragment comes from.
   2. Extract and clone the DOM subtree that corresponds to the fragment to be included (for example, by searching for a "th:fragment" attribute). This corresponds roughly with the code you included in your message.
   3. Insert such DOM subtree into the host template, as children of the "th:include" or "th:substituteby" tag.
   4. Continue execution (traversing nodes in-line) as if the newly included nodes were a part of the executed template from the beginning.

Hope this helps.

Regards,
Daniel.
Reply | Threaded
Open this post in threaded view
|

Re: Processing loaded fragment

Sorceror
I've found it after hours of debugging :)..
When I do extract fragment content and append it to current processed element in DOM tree, all new nested nodes are skipped (better, first node is skipped and with that node, all its children are skipped as well). When traversing continues (point 4.), this part of code is processed in Node class:

void processNode(final Arguments arguments, final boolean processTextNodes, final boolean processCommentNodes) {
        // some code
        if (!isPrecomputed()) {
            precomputeNode(arguments.getConfiguration());
        }

        if (!isSkippable()) {
                // processing itself
                ...
               
                // move processing to children, but does not happend if node is marked as skippable
                doAdditionalProcess(executionArguments, executionArguments.getProcessTextNodes(), executionArguments.getProcessCommentNodes());
        }
}

Because nodes extracted from fragment are not marked as precomputed, precomputeNode method follows:

final void precomputeNode(final Configuration configuration) {
    if (!isPrecomputed()) {
        this.processors = configuration.computeProcessorsForNode(this);
        if (this.processors == null || this.processors.size() == 0) {
            this.skippable = true;
        } else {
            unsafeSetSkippable(false);
        }
            setPrecomputed(true);
    }
    doAdditionalPrecomputeNode(configuration);
}

Because node is not marked as precomputed, method computeProcessorsForNode() is called. This method returns null, then node is marked as skippable. As comment says, siblings of current node should set this node back as non-skippable in doAdditionalPrecomputeNode() method. Unfortunately this does not happen.

After some more debugging I've found, that on all extracted nodes from fragment have to be called method node.setProcessable(true); to avoid first node to be set as skippable.
I don't know if this is a bug or feature, I would appreciate if you'll let me know :).

Anyway, thymeleaf is neat template engine!
Only feature I do really miss is to send some arguments to fragments..

Thanks
Paul
Reply | Threaded
Open this post in threaded view
|

Re: Processing loaded fragment

Emanuel
Administrator
What kind of arguments are you wanting to send?  Most things can be sent to an included fragment by using the th:with attribute alongside your th:include, eg:

<div th:include="myfragments.html :: somefragment" th:with="arg1='text', arg2=${bean.value}"></div>

In the example above, the arg1 and arg2 values can be used in the myfragments.html :: somefragment part.

If you're wanting to send HTML content to a fragment, then you can use the layout:include attribute of the layout dialect (https://github.com/ultraq/thymeleaf-layout-dialect) to do that:

<div layout:include="myfragments.html :: somefragment" th:with="arg1='text', arg2=${bean.value}">
  <p layout:fragment="body-content">This text will end-up in the 'body-content' fragment of the specified fragment.</p>
</div>