Layout templates

AcrossWebModule provides support for simple layout processing. A controller method can be linked to a specific template which will determine some pre- and post-processing that might be done. The base infrastructure is provided by WebTemplateProcessor, WebTemplateInterceptor and WebTemplateRegistry.

Creating a template

The easiest way to create a new template is to create a bean extending LayoutTemplateProcessorAdapterBean. Your implementation should get a unique name and a path to the template view file. This basic template implementation has adapter methods for registering of web resources and generating one or more menu structures.

Example layout template implementation
@Component
public class MyTemplate extends LayoutTemplateProcessorAdapterBean {

    public MyTemplate() {
        super( "MyTemplate", "th/mysite/layout" );
    }

    @Override
    protected void registerWebResources( WebResourceRegistry registry ) {
        registry.add( WebResource.CSS, "/static/mysite/css/main.css", WebResource.VIEWS );
    }

    @Override
    protected void buildMenus( MenuFactory menuFactory ) {
        menuFactory.buildMenu( "topMenu" );
    }
}

When the template is applied it will take the original view name and put it as a model attribute with the key childPage. The actual template view can then dispatch to the original view (or parts of it) when building the final layout.

Example Thymeleaf template that renders the content fragment of the original view
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>My Site</title>

    <script th:each="javascript : ${webResourceRegistry.getResources('javascript')}" th:src="@{${javascript.data}}"
            th:if="${javascript.location != 'inline'}"></script>

    <link rel="stylesheet" type="text/css" th:each="css : ${webResourceRegistry.getResources('css')}"
          th:href="@{${css.data}}"/>
</head>
<body>
    <div th:replace="${childPage} :: content">
        Insert original view
    </div>

    <script th:each="javascript : ${webResourceRegistry.getResources('javascript-page-end')}" th:src="@{${javascript.data}}"
            th:if="${javascript.location != 'inline'}"></script>
</body>
</html>

After the initial registerWebResources(WebResourceRegistry) has been executed by the template component, an BuildTemplateWebResourcesEvent with the template name will be published. This allows event listeners to add additional resources for a specific template.

Linking a template to a controller

Any controller method can specify the template to use by setting the @Template annotation with the unique template name. The @Template annotation can be set on the @Controller itself (in which case it will apply to all mapping methods) or on mapping method directly. The latter will always take precedence.

You can always clear a template from being applied by setting a @ClearTemplate annotation on a method. Special Spring MVC return options (like methods annotated with @ResponseBody) will automatically suppress the template.
Methods in a @Controller do not take any @Template annotations of their @ControllerAdvice beans into account and vice versa.

Linking a template to an exception handler

Just like @Template can be used on any @RequestMapping method, it can also be used on a @ExceptionHandler method. Since an exception handler uses its own template, a template might be executed twice if an exception occurs. Initially a template might have been executed for the controller method. In case of an exception, that template will be cleared and a new WebResourceRegistry will be created for the @ExceptionHandler along with the optional template.

Registering the default template

If no explicit template is specified, the default template will be used it there is one. The default template must be set on the WebTemplateRegistry. set on the WebTemplateRegistry.

Example template registering itself as default
@Component
public class MyTemplate extends LayoutTemplateProcessorAdapterBean {
    ...

    @Autowired
    public void registerAsDefaultTemplate( WebTemplateRegistry webTemplateRegistry ) {
        webTemplateRegistry.setDefaultTemplateName( getName() );
    }

    ...
}