When we use the Context.render
method Ratpack's rendering mechanism kicks in. The type of the argument we pass to the render
method is used to look up the correct renderer. The renderer implements the Renderer
interface and provides the real output. We can add functionality that can work with the object of the Renderer
implementation before the actual output is created. We do this by adding a class or object to the registry that implements the RenderableDecorator
interface. The interface has a method decorate
that accepts the Context
and object that needs to be rendered. The code is invoked after the Context.render
method, but before the Renderer.render
method. This is especially useful when we use template renderers with a view model and with a RenderableDecorator
implementation we can augment the view model with some general attributes.
Suppose we have a Ratpack application that uses the Groovy text template engine provided by the TextTemplateModule
. The module adds a Renderer
for TextTemplate
objects. Let's write a RenderableDecorator
implementation for the TextTemplate
, where we add an extra attribute createdOn
to the view model:
// File: src/main/groovy/com/mrhaki/ratpack/CreatedOnRendererDecorator.groovy package com.mrhaki.ratpack import ratpack.exec.Promise import ratpack.groovy.template.TextTemplate import ratpack.handling.Context import ratpack.render.RenderableDecorator import java.time.Clock import java.time.LocalDateTime import java.time.format.DateTimeFormatter /** * Add extra attribute to view model for all TextTemplate renderers. */ class CreatedOnRendererDecorator implements RenderableDecorator<TextTemplate> { /** * Apply this decorator for TextTemplate renderers. * * @return TextTemplate class. */ @Override Class<TextTemplate> getType() { return TextTemplate } /** * Add an extra attribute createdOn to the view model with the current * date and time. * * @param context Context to get Clock instance for this Ratpack application from. * @param template Template with view model to extend. * @return Promise with new TextTemplate instance with the extended view model. */ @Override Promise<TextTemplate> decorate(final Context context, final TextTemplate template) { final footerModel = [createdOn: createdOn(context)] return Promise.value( new TextTemplate( template.model + footerModel, template.id, template.type)) } /** * Create formatted date/time String based on * the Clock available on the Ratpack registry. * * @param context Context to get Clock instance from. * @return Formatted date/time String. */ private String createdOn(final Context context) { final Clock clock = context.get(Clock) final LocalDateTime now = LocalDateTime.now(clock) final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return formatter.format(now) } }
To use this decorator we must add it to the registry in our Ratpack application. We also use the RenderableDecorator.of
method to create a decorator for our application directory in our Ratpack Groovy DSL:
// File: src/ratpack/Ratpack.groovy import com.mrhaki.ratpack.CreatedOnRendererDecorator import ratpack.groovy.template.TextTemplate import ratpack.groovy.template.TextTemplateModule import ratpack.render.RenderableDecorator import ratpack.util.RatpackVersion import static ratpack.groovy.Groovy.groovyTemplate import static ratpack.groovy.Groovy.ratpack ratpack { bindings { // Use Groovy's simple text template engine. module TextTemplateModule // Use class that implements the // RenderableDecorator interface. bind CreatedOnRendererDecorator // Create RenderableDecorator instance using the // RenderableDecorator.of method. // Here we add the model attribute createdWith. bindInstance RenderableDecorator.of(TextTemplate) { context, template -> new TextTemplate( template.model + [createdWith: "Ratpack ${RatpackVersion.version}"], template.id, template.type) } } handlers { get { // Set model attributes used on the template. final model = [title: 'Ratpack Application', welcomeMessage: 'Welcome to Ratpack'] render groovyTemplate(model, 'index.html') } files { dir "public" } } }
To complete the example we create the following index.html
file in the directory src/ratpack/templates
:
<!doctype html> <html class="no-js" lang=""> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>${model.title}</title> </head> <body> <header> <h1>${model.welcomeMessage}</h1> </header> <section> <p>Some sample text for the template.</p> </section> <footer> <p>Page is created with ${model.createdWith} at ${model.createdOn}.</p> </footer> </body> </html>
Written with Ratpack 1.1.1.