Rendering Fragments from Controller (Jersey 2.25.1, Thymeleaf 3.0.5.RELEASE)

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

Rendering Fragments from Controller (Jersey 2.25.1, Thymeleaf 3.0.5.RELEASE)

Arek
Hello,

i have to implement a controller which only returns a rendered fragment. In Thymeleaf 2 we can use StandardFragmentProcessor but this class disappeared in Thymeleaf 3.
I couldnt find a documentation how to migrate the StandardFragmentProcessor to a new processor or similar.

Is there an example how to migrate this or how to handle this issue for returning rendered fragments?

Thanks a lot!
Best regards
Arek
Reply | Threaded
Open this post in threaded view
|

Re: Rendering Fragments from Controller (Jersey 2.25.1, Thymeleaf 3.0.5.RELEASE)

Metroids
I've never used a StandardFragmentProcessor... but I don't think you need it to return a fragment.

Instead of returning "name_of_template" in your controller, you should return "name_of_template :: fragment_name" and then the controller will just return that fragment.

(This may have worked in thymeleaf 2, I'm not really sure.  It definitely works in 3).
Reply | Threaded
Open this post in threaded view
|

Re: Rendering Fragments from Controller (Jersey 2.25.1, Thymeleaf 3.0.5.RELEASE)

Arek
Metroids wrote
Instead of returning "name_of_template" in your controller, you should return "name_of_template :: fragment_name" and then the controller will just return that fragment.
This only works if you use Thymeleaf in a Spring Controller. In my project we use Jersey Controllers. So just returning a String with the View will not work, even without using Fragments.

I found it out for myself. I've build a spring-boot project with thymeleaf 3.0 and checked out how the "template::fragment" expression is processed.

So for anybody searching for the alternative from the StandardFragmentProcessor, lookout for this classes:
IStandardExpressionParser, StandardExpressions, FragmentExpression and ExecutedFragmentExpression.
With the ExecutedFragmentExpression object you can render only the fragment with:
Set resolved = FragmentExpression.resolveFragments(executedFragment);

you can pass the resolved object to the templateEngine.process() and it's done!

Have fun
Best regards
Arek
Reply | Threaded
Open this post in threaded view
|

Re: Rendering Fragments from Controller (Jersey 2.25.1, Thymeleaf 3.0.5.RELEASE)

Metroids
I was curious about this... I found a way to do it without implementing any classes.  Would this do what you want?  I'm specifying a template ("template") and a fragment ("fragment2").  And the output is just a fragment.

HTML (template.html):
<!DOCTYPE html>
<html>
    <body>
        <section th:fragment="fragment1">
            <div th:text="${message}" />
        </section>
        
        <section th:fragment="fragment2">
            <div th:text="${message}" />
        </section>
    </body>
</html>

Java:
package student.jersey;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;

@Path("/hello")
public class Thymeleaf {
    @GET
    @Path("/world")
    public Response getMsg() {
        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        
        resolver.setCacheable(false);
        resolver.setPrefix("template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setTemplateMode("HTML5");
        resolver.setOrder(1);
        resolver.setCheckExistence(true);

        TemplateEngine engine = new TemplateEngine();
        Set<ITemplateResolver> templateResolvers = new HashSet<>();
        templateResolvers.add(resolver);
        engine.setTemplateResolvers(templateResolvers);
        
        final Context ctx = new Context();
        ctx.setVariable("message", "world");
        
        return Response.status(200).entity(engine.process("template", new HashSet<String>(Arrays.asList("fragment2")), ctx)).build();
    }
}

Output:
<section>
    <div >world</div>
</section>
Reply | Threaded
Open this post in threaded view
|

Re: Rendering Fragments from Controller (Jersey 2.25.1, Thymeleaf 3.0.5.RELEASE)

Arek
Awesome, looks really easy. I have to try that out! Where did you find that in the documentation? Thanks!