Add script based templating support

This commit adds support for script based templating. Any templating
library running on top of a JSR-223 ScriptEngine that implements
Invocable like Nashorn or JRuby could be used.

For example, in order to render Mustache templates thanks to the Nashorn
Javascript engine provided with Java 8+, you should declare the following
configuration:

@Configuration
@EnableWebMvc
public class MustacheConfig extends WebMvcConfigurerAdapter {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.scriptTemplate();
	}

	@Bean
	public ScriptTemplateConfigurer configurer() {
		ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
		configurer.setEngineName("nashorn");
		configurer.setScripts("mustache.js");
		configurer.setRenderObject("Mustache");
		configurer.setRenderFunction("render");
		return configurer;
	}
}

The XML counterpart is:

<beans>
	<mvc:annotation-driven />

	<mvc:view-resolvers>
		<mvc:script-template />
	</mvc:view-resolvers>

	<mvc:script-template-configurer engine-name="nashorn" render-object="Mustache" render-function="render">
		<mvc:script location="mustache.js" />
	</mvc:script-template-configurer>
</beans>

Tested with:
 - Handlebars running on Nashorn
 - Mustache running on Nashorn
 - React running on Nashorn
 - EJS running on Nashorn
 - ERB running on JRuby
 - String templates running on Jython

Issue: SPR-12266
This commit is contained in:
Sebastien Deleuze
2015-03-17 18:14:05 +01:00
parent b6327acec8
commit a3159dfbf2
36 changed files with 1792 additions and 16 deletions

View File

@@ -26,6 +26,7 @@
<mvc:freemarker />
<mvc:velocity />
<mvc:groovy />
<mvc:script-template />
</mvc:view-resolvers>
<mvc:tiles-configurer check-refresh="true">
@@ -40,5 +41,7 @@
<mvc:groovy-configurer />
<mvc:script-template-configurer engine-name="nashorn" render-function="render"/>
</beans>

View File

@@ -13,6 +13,7 @@
<mvc:freemarker prefix="freemarker-" suffix=".freemarker" view-names="my*,*Report" />
<mvc:velocity cache-views="false" />
<mvc:groovy />
<mvc:script-template />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
<ref bean="customResolver" />
</mvc:view-resolvers>
@@ -33,5 +34,9 @@
<mvc:groovy-configurer resource-loader-path="/test" cache-templates="false" auto-indent="true" />
<mvc:script-template-configurer engine-name="nashorn" render-object="Mustache" render-function="render" charset="ISO-8859-1" resource-loader-path="classpath:">
<mvc:script location="/META-INF/resources/webjars/mustachejs/0.8.2/mustache.js" />
</mvc:script-template-configurer>
</beans>

View File

@@ -0,0 +1,21 @@
require 'erb'
require 'ostruct'
require 'java'
# Renders an ERB template against a hashmap of variables.
# template should be a Java InputStream
def render(template, variables)
context = OpenStruct.new(variables).instance_eval do
variables.each do |k, v|
instance_variable_set(k, v) if k[0] == '@'
end
def partial(partial_name, options={})
new_variables = marshal_dump.merge(options[:locals] || {})
Java::Pavo::ERB.render(partial_name, new_variables)
end
binding
end
ERB.new(template).result(context);
end

View File

@@ -0,0 +1 @@
<html><head><title><%= title %></title></head><body><p><%= body %></p></body></html>

View File

@@ -0,0 +1,5 @@
// TODO Manage compiled template cache
function render(template, model) {
var compiledTemplate = Handlebars.compile(template);
return compiledTemplate(model);
}

View File

@@ -0,0 +1 @@
<html><head><title>{{title}}</title></head><body><p>{{body}}</p></body></html>

View File

@@ -0,0 +1 @@
<html><head><title>{{title}}</title></head><body><p>{{body}}</p></body></html>

View File

@@ -0,0 +1,5 @@
from string import Template
def render(template, model):
s = Template(template)
return s.substitute(model)

View File

@@ -0,0 +1 @@
<html><head><title>$title</title></head><body><p>$body</p></body></html>

View File

@@ -0,0 +1,5 @@
var global = this;
var console = {};
console.debug = print;
console.warn = print;
console.log = print;

View File

@@ -0,0 +1,13 @@
function render(template, model) {
// Create a real Javascript Object from the model Map
var data = {};
for(var k in model) data[k]=model[k];
var element = React.createElement(eval(template), data);
// Should use React.renderToString in production
return React.renderToStaticMarkup(element);
}
function renderJsx(template, model) {
var jsTemplate = JSXTransformer.transform(template).code;
return render(jsTemplate, model);
}

View File

@@ -0,0 +1,5 @@
React.createClass({
render: function() {
return React.createElement("html", null, React.createElement("head", null, React.createElement("title", null, this.props.title)), React.createElement("body", null, React.createElement("p", null, this.props.body)));
}
});

View File

@@ -0,0 +1,5 @@
React.createClass({
render: function() {
return <html><head><title>{this.props.title}</title></head><body><p>{this.props.body}</p></body></html>
}
});